Commit Graph

5 Commits

Author SHA1 Message Date
Ilia Denisov c305363ccd feat(lobby): enter the game immediately and wait for the opponent inside it
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 1m4s
Quick auto-match no longer waits on a separate screen: Enqueue opens a real game seating the caller with an empty opponent seat (new game status 'open') and the player enters it at once. A second human searching the same variant+rule joins that open game; otherwise a background reaper seats a robot after a 90s + random 0-90s wait, pushing a new in-app opponent_joined event that fills the opponent card and re-enables resign and chat in place.

Matchmaking state is now the open games in the database (the in-memory pool, lobby.poll and lobby.cancel are gone), serialised by a per-bucket advisory lock. While a game is open the starter may move on their turn, but resign, chat and nudge are refused; the lobby and opponent card show "searching for opponent".

Schema edited in the baseline (no prod data): 'open' status, nullable game_players.account_id for the empty seat, and a games.open_deadline_at stamp; jet code regenerated.
2026-06-12 16:00:22 +02:00
Ilia Denisov 74455c7b12 feat: "multiple words per turn" rule for Russian games
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m10s
Add a per-game rule chosen on New Game for Russian variants (default off = the
single-word rule; on = standard Scrabble). Off, only the main word along the play
direction is validated and scored; perpendicular cross-words are ignored,
including in robot move generation. The rule rides every create and enqueue
request and joins the matchmaking key, so games and auto-match stay one uniform
path; "Russian-only" is a UI affordance (English always sends standard and shows
no toggle).

- Engine: consume scrabble-solver v1.1.0's PlayOptions{IgnoreCrossWords}, threaded
  through engine.Options.MultipleWordsPerTurn -> playOpts() into validate, score
  and generate.
- Backend: thread the flag through game CreateParams/Game + store (games column),
  lobby InvitationSettings + invitation row, and the matchmaker queue key (variant
  + rule); persisted, so a rebuilt-from-journal game keeps it. Baseline migration
  gains multiple_words_per_turn (DB not versioned); jet regenerated.
- Edge: multiple_words_per_turn added to the EnqueueRequest / CreateInvitationRequest
  FlatBuffers tables (Go + TS regenerated) and threaded through the gateway.
- UI: a "Multiple words per turn" toggle on New Game, shown for Russian variants
  only (auto-match and friend invite), default off; English silently sends standard.
- Tests: backend engine/matchmaker; UI unit (gating) + Playwright e2e (solver
  corner-case + GCG fixtures ship in v1.1.0). Docs + PRERELEASE tracker updated.
2026-06-12 02:17:30 +02:00
Ilia Denisov d733ce3119 Stage 8: UI social/account/history surfaces
Tests · Go / test (push) Successful in 7s
Tests · Integration / integration (push) Successful in 13s
Tests · UI / test (push) Successful in 16s
Wire the deferred Stage 7 surfaces end-to-end (UI -> gateway transcode ->
backend REST -> existing domain services): friends (incl. one-time friend
codes), per-user blocks, friend-game invitations, profile editing + email
binding, the statistics screen, and the in-game history + GCG export.

Friends gain two add paths (interview decision, a deliberate plan change):
one-time 6-digit codes (friend_codes table, 12h TTL, single-use, rate-limited
redeem); and play-gated requests (shared game required) where an explicit
decline is permanent, an ignored request lapses after 30 days, and a code
bypasses a decline. Migration 00006 widens friendships_status_chk and adds
friend_codes.

Lobby notification badge is poll + push: a new generic `notify` event drives
it live; the client polls on open/focus. Language stays a single Settings
control that writes through to the durable account's preferred_language. GCG
export is finished-only (game.ErrGameActive) and shares/downloads the .gcg file.

Tests: backend unit + inttest (friend gate/decline/code, ListInvitations,
GetStats, GCG gate), gateway transcode round-trips + notify constructor, UI
vitest (codecs, win-rate, share choice) + Playwright social specs. Docs: PLAN
(Stage 8 done + refinements + TODO-5), ARCHITECTURE, FUNCTIONAL(+ru), UI_DESIGN,
TESTING, module READMEs.
2026-06-03 19:47:40 +02:00
Ilia Denisov 85baabe4ba Stage 5: robot opponent (pool, seed-derived strategy, move driver, matchmaker substitution)
Tests · Go / test (push) Successful in 6s
Tests · Integration / integration (push) Successful in 10s
Tests · Go / test (pull_request) Successful in 5s
Tests · Integration / integration (pull_request) Successful in 10s
- internal/robot: durable kind='robot' account pool (migration 00004); every
  per-game and per-turn choice derived deterministically from the game seed
  (restart-stable FNV mix); a background move driver; margin targeting (band
  1-30, closest-to-band); right-skewed [2,90]min delays (median ~10m);
  opponent-anchored sleep with +/-3h drift; daytime nudge reply + proactive
  12h nudge; friend/chat blocked via profile toggles.
- engine.Candidates (decoded ranked plays); game.Candidates + RobotTurns;
  social.LastNudgeAt.
- matchmaker: 10s wait then robot substitution (reaper) + Poll delivery seam.
- config (BACKEND_ROBOT_DRIVE_INTERVAL, BACKEND_LOBBY_ROBOT_WAIT,
  BACKEND_LOBBY_REAPER_INTERVAL); main wiring + boot-time pool provisioning.
- metrics: robot account_stats (authoritative balance) + robot_games_finished_total
  OTel counter + per-finish log.
- docs: PLAN, ARCHITECTURE, FUNCTIONAL(+ru), TESTING, README; account.go comment.
- tests: robot strategy units, matchmaker reaper/Poll, engine.Candidates; inttest
  robot full-game / substitution / proactive-nudge.
2026-06-02 21:02:20 +02:00
Ilia Denisov bfa8797f8c Stage 4: lobby & social (matchmaking, friends, blocks, chat+nudge, invitations, profile, email, multi-player drop-out)
Tests · Go / test (push) Successful in 6s
Tests · Integration / integration (push) Successful in 9s
Tests · Go / test (pull_request) Successful in 6s
Tests · Integration / integration (pull_request) Successful in 9s
Engine: multi-player drop-out-and-continue with a per-game tile disposition (remove default / return), resigned seats skipped and excluded from the win, leaver rack never revealed; 2-player behaviour unchanged.

New domains (service/store, no HTTP yet): internal/social (friend request/accept graph, per-user blocks, per-game chat with nudge as a message kind, content filter via mvdan.cc/xurls/v2 + leet/separator normaliser + phone heuristic) and internal/lobby (in-memory variant-keyed matchmaking pool, friend-game invitations invite->accept with lazy 7-day expiry). account gains profile editing and the email confirm-code flow (Mailer seam: SMTP or log mailer).

Migration 00003_social.sql + regenerated jet. main wires the new services into the server (accessors for the Stage 6 handlers); robot substitution stays in Stage 5, REST/stream/push in Stage 6/8. Docs (PLAN, ARCHITECTURE, FUNCTIONAL+ru, TESTING, README) updated.
2026-06-02 19:29:30 +02:00