Commit Graph

67 Commits

Author SHA1 Message Date
adlee-was-taken
ea34ddf8e4 Fix swap animation stutter and remove 1s server-side dead delay
- Remove unused card_revealed broadcast + 1s asyncio.sleep in swap handler
  (client never handled this message, causing pure dead wait before game_state)
- Defer swap-out (opacity:0) on hand cards to onStart callback so overlay
  covers the card before hiding it — eliminates visual gap for all players
- Defer heldCardFloating visibility hide to onStart — held card stays visible
  until animation overlay replaces it
- Thread onStart callback through animateUnifiedSwap → _runUnifiedSwap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:47:26 -05:00
adlee-was-taken
215849703c Add inline comments across client and server codebase
Full-codebase commenting pass focused on the tricky, fragile, and
non-obvious spots: animation coordination flags in app.js, AI decision
safety checks in ai.py, scoring evaluation order in game.py, animation
engine magic numbers in card-animations.js, and server infrastructure
coupling in main.py/handlers.py/room.py. No logic changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:17:19 -05:00
adlee-was-taken
6461a7f0c7 Add metered open signups, per-IP limits, and auth security hardening
Enables public beta signup metering: DAILY_OPEN_SIGNUPS env var controls
how many users can register without an invite code per day (0=disabled,
-1=unlimited, N=daily cap). Invite codes always bypass the limit.

Also adds per-IP signup throttling (DAILY_SIGNUPS_PER_IP, default 3/day)
and fail-closed rate limiting on auth endpoints when Redis is down.

Client dynamically fetches /api/auth/signup-info to show invite field
as optional with remaining slots when open signups are enabled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:28:28 -05:00
adlee-was-taken
3dcad3dfdf Fix round-end reveal timing: pause after last play, handle deferred state
Two issues fixed:

1. renderGame() was called before the lastPlayPause delay, causing the
   board to jump to final card positions while the swap animation was
   still visually playing. Moved renderGame() to after the wait+pause.

2. When the local player makes the final play, their swap animation
   defers the round_over game_state to pendingGameState. The deferred
   state bypassed the round-end intercept, so preRevealState was never
   set — causing the scoresheet to appear immediately without the
   reveal animation. Now completeSwapAnimation checks for round_over
   transitions and sets preRevealState. Also added a wait loop in
   runRoundEndReveal for robustness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:35:02 -05:00
adlee-was-taken
b129aa4f29 Fix opponent draw-from-discard animation showing wrong card
Force discard pile DOM update before draw animation starts to prevent
stale card display when previous swap animation blocked renderGame.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:26:24 -05:00
adlee-was-taken
77cbefc30c Improve initial card flip animation appearance
Style the flip overlay's front face to match player hand cards (gradient
background, proper border/shadow) instead of using generic card-front
styles. Hide the underlying card during the animation so the green table
shows through the flip rather than a white card peeking behind it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:29:09 -05:00
adlee-was-taken
e2c7a55dac Fix held card displacement in landscape and tooltip crash
The turn pulse shake was targeting .discard-stack, which is an ancestor of
#held-card-floating. A CSS transform on any ancestor breaks position:fixed,
causing the held card to render far from the deck area. Now target #discard
directly instead.

Also fix duplicate getCardPointValue methods — the 3-arg scoring version
shadowed the 1-arg tooltip version, leaving cardValues undefined on hover.

Add staging deploy script (rsync working tree, no git pull needed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:16:20 -05:00
adlee-was-taken
8d5b2ee655 Fix AI knock decisions and improve round-end animations
Fix dumb AI knocks (e.g. Maya knocking on 13 points) by adding opponent
threat checks and a hard cap of 10 to should_knock_early(). Remove dead
should_go_out_early() call whose return value was never used. Retune knock
chance tiers to be more conservative at higher projected scores.

On the client side, fix round-end reveal sequencing so the last player's
swap/discard animation plays before the reveal sequence starts, and prevent
re-renders from clobbering swap animations during reveals. Also make the
turn-pulse shake configurable via timing-config and target only cards.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:07:57 -05:00
adlee-was-taken
cb49fd545b Add gradient backgrounds to all status messages, match final-turn badge size
- Default gradient on base .status-message (dark green)
- Add round-over/game-over (gold) and reveal (purple) gradient styles
- Tag action prompts (swap, flip, discard) as your-turn type
- Match final-turn badge font size and padding to status message on mobile
- Hide status message when empty

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:31:20 -05:00
adlee-was-taken
21985b7e9b Route all lobby transitions through showLobby() for animation cleanup
game_ended, queue_left, and cancelMatchmaking were calling
showScreen('lobby') directly, bypassing the cancelAll() cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:12:39 -05:00
adlee-was-taken
56305424ff Thorough animation cleanup when leaving game
- Tag deal container with class so cleanup() can find and remove it
- Remove .traveling-card and .deal-anim-container overlays in cleanup()
- Restore opacity/visibility on cards hidden mid-animation
- Reset all animation flags (dealAnimationInProgress, etc.) in showLobby()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:09:37 -05:00
adlee-was-taken
0bfe9d5f9f Cancel animations on game leave to prevent overlay flash on lobby
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:06:11 -05:00
adlee-was-taken
a0bb28d5eb Fix opponent swap animation instant shrink on mobile portrait
Let overlay card start at deck size and smoothly scale down to opponent
card size during the arc, instead of instantly shrinking before animating.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:00:20 -05:00
adlee-was-taken
1de282afc2 Change mobile rules pill default text from "S" to "RULES"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:17:41 -05:00
adlee-was-taken
9b0a8295eb Add mobile rules indicator pill and drawer
Shows "S" (standard) or "!" (house rules) in the mobile bottom bar.
Tapping opens a drawer with the full active rules list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:14:09 -05:00
adlee-was-taken
e2a90c0f34 Fix knocker highlight not showing on opponents
markKnocker() was called before opponent areas were rebuilt by
innerHTML='', so the is-knocker class and OUT badge were immediately
destroyed. Move markKnocker to after opponent areas are created.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:37:33 -05:00
adlee-was-taken
9866fb8e92 Move discard button below held card on mobile portrait
Position the button centered beneath the held card instead of to the
right side. Reset writing-mode to horizontal and add width:auto.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:39:33 -05:00
adlee-was-taken
4a5cfb68f1 Set held card offset to 0.48 on mobile portrait
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:35:58 -05:00
adlee-was-taken
ebb00f613c Lower held card offset to 0.55 on mobile portrait
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:34:00 -05:00
adlee-was-taken
98aa0823ed Set held card mobile portrait offset back to 0.65
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:33:36 -05:00
adlee-was-taken
4a3d62e26e Nudge held card up slightly to clear DRAW/DISCARD labels
Increase mobile portrait overlap offset from 0.65 to 0.8.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:32:46 -05:00
adlee-was-taken
d958258066 Lower held card position to just above the labels on mobile portrait
Reduce overlap offset from 1.15 to 0.65 so the held card sits at the
DRAW/DISCARD label level rather than up in the opponents area.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:30:33 -05:00
adlee-was-taken
26bc151458 Move held card to gap above deck area on mobile portrait
Position the held card a full card height above the deck (1.15x offset)
so it sits in the space between opponents and the draw/discard piles.
All three position calculations (app.js x2, card-animations.js) are
synced so draw animations land at the correct held position.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:27:45 -05:00
adlee-was-taken
3414bfad1a Sync held card position across all animation paths for mobile portrait
Update getHoldingRect() in card-animations.js and the second held card
positioning path in app.js to use the same reduced overlap offset on
mobile portrait. All three places that compute the held position now
use 0.15 on mobile-portrait vs 0.35 on desktop/landscape.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:12:46 -05:00
adlee-was-taken
ecad259db2 Lower held card position and add opponent row padding on mobile
Reduce held card overlap offset from 0.35 to 0.15 on mobile portrait
so it doesn't cover the second row of opponents. Increase bottom
padding on opponents row from 6px to 12px for more breathing room.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:10:11 -05:00
adlee-was-taken
10825e8b82 Add opponent-turn class to status message in renderGame
The status was set without a type class in renderGame(), overriding
the styled version from updateStatusFromGameState() on every state
update. Now the purple background shows consistently for opponent turns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:00:38 -05:00
adlee-was-taken
21362ba125 Fix pair chime not playing for local player's own swaps
The swap animation defers state updates to pendingGameState, which
bypassed checkForNewPairs entirely. Now pair detection runs when the
deferred state is applied after the swap animation completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:47:43 -05:00
adlee-was-taken
2dcdaf2b49 Remove turns remaining counter from FINAL TURN badge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:46:03 -05:00
adlee-was-taken
1fa13bbe3b Play pair sound before element check and add pair detection debug log
Sound was gated behind element check which may fail during swap
animation when card DOM elements are replaced by overlays.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:41:17 -05:00
adlee-was-taken
a76fd8da32 Hide bottom bar during scoresheet modal and compact mobile layout
Bottom bar is hidden when the hole results modal opens and restored
when dismissed. Also adds mobile-specific compact styles for the
scoresheet: smaller cards, tighter spacing, reduced padding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:34:16 -05:00
adlee-was-taken
634d101f2c Play pair chime sound for all players including local player
firePairCelebration was only doing the visual animation but not playing
the pair sound. The sound was only played during score tallying.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:31:00 -05:00
adlee-was-taken
65b4af9831 Hide mobile bottom bar when drawer panels are open
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:38:14 -05:00
adlee-was-taken
7dc27fe882 Use window.innerHeight for mobile viewport height on Chrome Android
Set --app-height CSS custom property from window.innerHeight via JS,
which is the only reliable way to get the actual visible viewport on
Chrome Android. Falls back to 100vh if JS hasn't loaded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:28:58 -05:00
adlee-was-taken
b4e9390f16 Show both KNOCKED and LOW SCORE badges when knocker wins hole
Also fix opponent areas shifting between rows on mobile by giving them
a fixed 120px width so name/score changes don't cause reflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:54:18 -05:00
adlee-was-taken
797d1e0280 Add copy invite link button and auto-populate invite code from URL
Admin panel gets "Copy Link" button on active invites that copies
a signup URL with ?invite= param. Client auto-opens signup form
with invite code pre-filled when visiting that link.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:54:06 -05:00
adlee-was-taken
538ca51ba5 Add forgot/reset password UI and Resend email config
- Forgot password form in auth modal with email input
- Reset password form handles token from email link
- /reset-password route serves index.html for SPA
- EMAIL_FROM env var in docker-compose
- Success/error feedback for both flows

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:51:58 -05:00
adlee-was-taken
ac2d53b404 Move Hole indicator and End Game button to mobile bottom bar
- Add round info and leave button to mobile-bottom-bar HTML
- Hide .round-info and #leave-game-btn from header on mobile
- Style round info as subtle text, leave button as red-tinted pill
- Slim down bottom bar: smaller gaps/padding to fit 4 items
- Sync round numbers and leave text via JS (renderGame + bindEvents)
- Frees up header space, reduces mobile crowding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:40:24 -05:00
adlee-was-taken
fb3bd53b0a Fix mobile animation card sizing and layout polish
- Fix animation overlay cards rendering at wrong size: base .card CSS
  (clamp 65px min) was overriding the inline dimensions set by JS.
  Add !important to .draw-anim-front/.draw-anim-back width/height: 100%
  so overlays always match their parent container size.
- Size opponent swap held card to match opponent card dimensions instead
  of defaulting to deck size (looked oversized on mobile)
- Shrink dealer chip on mobile (38px -> 20px) to fit opponent areas
- Make header more compact: smaller fonts, tighter gaps, nowrap on badges
- Bump deck/discard to 72x101px to match player card size on mobile
- Add spacing between header/opponents, and between deck area/player cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:11:39 -05:00
adlee-was-taken
4fcdf13f66 Fix mobile portrait layout: lobby overlap, deal animation, card font sizes
- Add renderGame() guard during deal animation to prevent DOM destruction
  mid-animation causing cards to pile up at wrong positions
- Push lobby content below fixed auth-bar (padding 15px -> 50px top)
- Scale player card font-size to 1.5rem/1.3rem for readable text on mobile
- Add full mobile portrait layout: bottom drawers, compact header, responsive
  card grid sizing, safe-area insets, and mobile detection via matchMedia
- Add cardFontSize() helper for consistent proportional font scaling
- Add mobile bottom bar with drawer toggles for standings/scores

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:52:44 -05:00
adlee-was-taken
62e7d4e1dd Fix End Game showing false 'Connection lost' error
Add _intentionalClose flag to suppress error when server closes
WebSocket after game_ended broadcast. Clean transition to lobby.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 20:39:43 -05:00
adlee-was-taken
bae5d8da3c Wire authManager into GolfGame instance for WebSocket token auth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 20:32:49 -05:00
adlee-was-taken
f68d0bc26d v3.1.0: Invite-gated auth, Glicko-2 ratings, matchmaking queue
- Enforce invite codes on registration (INVITE_ONLY=true by default)
- Bootstrap admin account for first-time setup
- Require authentication for WebSocket connections and room creation
- Add Glicko-2 rating system with multiplayer pairwise comparisons
- Add Redis-backed matchmaking queue with expanding rating window
- Auto-start matched games with standard rules after countdown
- Add "Find Game" button and matchmaking UI to client
- Add rating column to leaderboard
- Scale down docker-compose.prod.yml for 512MB droplet

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 20:02:10 -05:00
adlee-was-taken
c59c1e28e2 Smooth held card transition and scale font with card size
Remove scale(1.15) size jump on held card, keep gold border/glow highlight.
Set animation card font-size proportionally to card width so text matches
across deck, hand, and opponent card sizes. Animate font-size during swaps
so text scales smoothly as cards travel between different-sized positions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 00:14:27 -05:00
adlee-was-taken
850b8d6abf Standard-rules-only leaderboard with client unranked indicators
Only standard-rules games now count toward leaderboard stats. Games
with any house rule variant are marked "Unranked" in the active rules
bar, and a notice appears in the lobby when house rules are selected.
Also fixes game_logger duplicate options dicts (now uses dataclasses.asdict,
capturing all options including previously missing ones) and refactors
duplicated achievement-checking logic into shared helpers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:16:45 -05:00
adlee-was-taken
e1cca98b8b Fix client scoring to respect house rules for column pairs
Client-side scoring (points badge and score tally animation) ignored
house rules that modify pair behavior. Extract shared
calculateColumnScores() helper that mirrors server logic for
eagle_eye, negative_pairs_keep_value, wolfpack, four_of_a_kind,
and one_eyed_jacks rules. Server now sends scoring_rules flags
in game state.

Also fix opponent flip animation card font-size matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:34:40 -05:00
adlee-was-taken
8431cd6fd1 Speed up score animations and fix end-of-round UI
- Cut reveal/tally/celebration timings by ~50% for snappier round end
- Add dealAnimationInProgress flag to suppress flip prompts during deal
- Stop deck/discard pulse animation when round ends
- Update CLAUDE.md with animation race condition documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 08:45:44 -05:00
adlee-was-taken
c615c8b433 Fix animation race conditions and improve UI feedback
- Fix discard pile "do-si-do" race condition when CPU draws from discard
- Add isDrawAnimating flag for opponent draw animations
- Skip STEP 2 (discard detection) when draw from discard detected
- Fix deal animation using wrong rect (was using whole player area)
- Add player area highlight when it's their turn (green glow)
- Clear opponent animation flags when your_turn message received
- Hide discard pile during draw-from-discard animation
- Add comprehensive debug logging for animation flags

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 00:28:06 -05:00
adlee-was-taken
bc1b1b7725 Migrate animation system to unified anime.js framework
- Replace CSS transitions with anime.js for all card animations
- Create card-animations.js as single source for all animation logic
- Remove draw-animations.js (merged into card-animations.js)
- Strip CSS transitions from card elements to prevent conflicts
- Fix held card appearing before draw animation completes
- Make opponent/CPU animations match local player behavior
- Add subtle shake effect for turn indicator (replaces brightness pulse)
- Speed up flip animations by 30% for snappier feel
- Remove unnecessary pulse effects after draws/swaps

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:57:53 -05:00
adlee-was-taken
7b64b8c17c Timing and animation changes for a more natural feeling game with CPU opps.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 12:22:54 -05:00
Aaron D. Lee
6950769bc3 Version 2.0.0: Animation fixes, timing improvements, and E2E test suite
Animation fixes:
- Fix held card positioning bug (was appearing at bottom of page)
- Fix discard pile blank/white flash on turn transitions
- Fix blank card at round end by skipping animations during round_over/game_over
- Set card content before triggering flip animation to prevent flash
- Center suit symbol on 10 cards

Timing improvements:
- Reduce post-discard delay from 700ms to 500ms
- Reduce post-swap delay from 1800ms to 1000ms
- Speed up swap flip animation from 1150ms to 550ms
- Reduce CPU initial thinking delay from 150-250ms to 80-150ms
- Pause now happens after swap completes (showing result) instead of before

E2E test suite:
- Add Playwright-based test bot that plays full games
- State parser extracts game state from DOM for validation
- AI brain ports decision logic for automated play
- Freeze detector monitors for UI hangs
- Visual validator checks CSS states
- Full game, stress, and visual test specs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:33:28 -05:00