Update CLAUDE.md with AI safety checks and architecture
- Add AI decision safety checks documentation - Add simulation testing commands - Update architecture with services/, stores/, and new files - Add PostgreSQL to dependencies Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4ad508f84f
commit
7d28e83a49
92
CLAUDE.md
92
CLAUDE.md
@ -21,7 +21,13 @@ golfgame/
|
|||||||
├── server/ # Python FastAPI backend
|
├── server/ # Python FastAPI backend
|
||||||
│ ├── main.py # HTTP routes, WebSocket handling
|
│ ├── main.py # HTTP routes, WebSocket handling
|
||||||
│ ├── game.py # Game logic, state machine
|
│ ├── game.py # Game logic, state machine
|
||||||
│ └── ai.py # CPU opponent AI with timing/personality
|
│ ├── ai.py # CPU opponent AI with timing/personality
|
||||||
|
│ ├── simulate.py # AI simulation runner with stats
|
||||||
|
│ ├── game_analyzer.py # Query tools for game analysis
|
||||||
|
│ ├── stores/
|
||||||
|
│ │ └── event_store.py # PostgreSQL event sourcing
|
||||||
|
│ └── services/
|
||||||
|
│ └── game_logger.py # Game move logging to PostgreSQL
|
||||||
│
|
│
|
||||||
├── client/ # Vanilla JS frontend
|
├── client/ # Vanilla JS frontend
|
||||||
│ ├── app.js # Main game controller
|
│ ├── app.js # Main game controller
|
||||||
@ -33,14 +39,20 @@ golfgame/
|
|||||||
│ ├── style.css # Styles (NO card transitions)
|
│ ├── style.css # Styles (NO card transitions)
|
||||||
│ └── ANIMATIONS.md # Animation system documentation
|
│ └── ANIMATIONS.md # Animation system documentation
|
||||||
│
|
│
|
||||||
└── docs/v3/ # Feature planning documents
|
└── docs/
|
||||||
|
├── v2/ # V2 feature docs (event sourcing, auth, etc.)
|
||||||
|
└── v3/ # V3 feature planning documents
|
||||||
```
|
```
|
||||||
|
|
||||||
## Key Technical Decisions
|
## Key Technical Decisions
|
||||||
|
|
||||||
### Animation System
|
### Animation System
|
||||||
|
|
||||||
**All card animations use anime.js.** No CSS transitions on card elements.
|
**When to use anime.js vs CSS:**
|
||||||
|
- **Anime.js (CardAnimations)**: Card movements, flips, swaps, draws - anything involving card elements
|
||||||
|
- **CSS keyframes/transitions**: Simple UI feedback (button hover, badge entrance, status message fades) - non-card elements
|
||||||
|
|
||||||
|
**General rule:** If it moves a card, use anime.js. If it's UI chrome, CSS is fine.
|
||||||
|
|
||||||
- See `client/ANIMATIONS.md` for full documentation
|
- See `client/ANIMATIONS.md` for full documentation
|
||||||
- `CardAnimations` class in `card-animations.js` handles everything
|
- `CardAnimations` class in `card-animations.js` handles everything
|
||||||
@ -51,13 +63,50 @@ golfgame/
|
|||||||
- Server is source of truth
|
- Server is source of truth
|
||||||
- Client receives full game state on each update
|
- Client receives full game state on each update
|
||||||
- `state-differ.js` computes diffs to trigger appropriate animations
|
- `state-differ.js` computes diffs to trigger appropriate animations
|
||||||
- `isDrawAnimating` flag prevents UI updates during animations
|
|
||||||
|
### Animation Race Condition Flags
|
||||||
|
|
||||||
|
Several flags in `app.js` prevent `renderGame()` from updating the discard pile during animations:
|
||||||
|
|
||||||
|
| Flag | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `isDrawAnimating` | Local or opponent draw animation in progress |
|
||||||
|
| `localDiscardAnimating` | Local player discarding drawn card |
|
||||||
|
| `opponentDiscardAnimating` | Opponent discarding without swap |
|
||||||
|
| `opponentSwapAnimation` | Opponent swap animation in progress |
|
||||||
|
| `dealAnimationInProgress` | Deal animation running (suppresses flip prompts) |
|
||||||
|
|
||||||
|
**Critical:** These flags must be cleared in ALL code paths (success, error, fallback). Failure to clear causes UI to freeze.
|
||||||
|
|
||||||
|
**Clear flags when:**
|
||||||
|
- Animation completes (callback)
|
||||||
|
- New animation starts (clear stale flags)
|
||||||
|
- `your_turn` message received (safety clear)
|
||||||
|
- Error/fallback paths
|
||||||
|
|
||||||
### CPU Players
|
### CPU Players
|
||||||
|
|
||||||
- AI logic in `server/ai.py`
|
- AI logic in `server/ai.py`
|
||||||
- Configurable timing delays for natural feel
|
- Configurable timing delays for natural feel
|
||||||
- Multiple personality types affect decision-making
|
- Multiple personality types affect decision-making (pair hunters, aggressive, conservative, etc.)
|
||||||
|
|
||||||
|
**AI Decision Safety Checks:**
|
||||||
|
- Never swap high cards (8+) into unknown positions (expected value ~4.5)
|
||||||
|
- Unpredictability has value threshold (7) to prevent obviously bad random plays
|
||||||
|
- Comeback bonus only applies to cards < 8
|
||||||
|
- Denial logic skips hidden positions for 8+ cards
|
||||||
|
|
||||||
|
**Testing AI with simulations:**
|
||||||
|
```bash
|
||||||
|
# Run 500 games and check dumb move rate
|
||||||
|
python server/simulate.py 500
|
||||||
|
|
||||||
|
# Detailed single game output
|
||||||
|
python server/simulate.py 1 --detailed
|
||||||
|
|
||||||
|
# Compare rule presets
|
||||||
|
python server/simulate.py 100 --compare
|
||||||
|
```
|
||||||
|
|
||||||
## Common Development Tasks
|
## Common Development Tasks
|
||||||
|
|
||||||
@ -93,18 +142,35 @@ Adjust delays in `server/ai.py` `CPU_TIMING` dict.
|
|||||||
|
|
||||||
### No CSS Transitions on Cards
|
### No CSS Transitions on Cards
|
||||||
|
|
||||||
Cards animate via anime.js only. The following should NOT have `transition`:
|
Cards animate via anime.js only. The following should NOT have `transition` (especially on `transform`):
|
||||||
- `.card`, `.card-inner`
|
- `.card`, `.card-inner`
|
||||||
- `.real-card`, `.swap-card`
|
- `.real-card`, `.swap-card`
|
||||||
- `.held-card-floating`
|
- `.held-card-floating`
|
||||||
|
|
||||||
|
Card hover effects are handled by `CardAnimations.hoverIn()/hoverOut()` methods.
|
||||||
|
CSS may still use box-shadow transitions for hover glow effects.
|
||||||
|
|
||||||
|
### State Differ Logic (triggerAnimationsForStateChange)
|
||||||
|
|
||||||
|
The state differ in `app.js` detects what changed between game states:
|
||||||
|
|
||||||
|
**STEP 1: Draw Detection**
|
||||||
|
- Detects when `drawn_card` goes from null to something
|
||||||
|
- Triggers draw animation (from deck or discard)
|
||||||
|
- Sets `isDrawAnimating` flag
|
||||||
|
|
||||||
|
**STEP 2: Discard/Swap Detection**
|
||||||
|
- Detects when `discard_top` changes and it was another player's turn
|
||||||
|
- Triggers swap or discard animation
|
||||||
|
- **Important:** Skip STEP 2 if STEP 1 detected a draw from discard (the discard change was from REMOVING a card, not adding one)
|
||||||
|
|
||||||
### Animation Overlays
|
### Animation Overlays
|
||||||
|
|
||||||
Complex animations create temporary overlay elements:
|
Complex animations create temporary overlay elements:
|
||||||
1. Create `.draw-anim-card` positioned over source
|
1. Create `.draw-anim-card` positioned over source
|
||||||
2. Hide original card
|
2. Hide original card (or set `opacity: 0` on discard pile during draw-from-discard)
|
||||||
3. Animate overlay
|
3. Animate overlay
|
||||||
4. Remove overlay, reveal updated card
|
4. Remove overlay, reveal updated card, restore visibility
|
||||||
|
|
||||||
### Fire-and-Forget for Opponents
|
### Fire-and-Forget for Opponents
|
||||||
|
|
||||||
@ -113,12 +179,22 @@ Opponent animations don't block - no callbacks needed:
|
|||||||
cardAnimations.animateOpponentFlip(cardElement, cardData);
|
cardAnimations.animateOpponentFlip(cardElement, cardData);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Common Animation Pitfalls
|
||||||
|
|
||||||
|
**Card position before append:** Always set `left`/`top` styles BEFORE appending overlay cards to body, otherwise they flash at (0,0).
|
||||||
|
|
||||||
|
**Deal animation source:** Use `getDeckRect()` for deal animations, not `getDealerRect()`. The dealer rect returns the whole player area, causing cards to animate at wrong size.
|
||||||
|
|
||||||
|
**Element rects during hidden:** `visibility: hidden` still allows `getBoundingClientRect()` to work. `display: none` does not.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- FastAPI
|
- FastAPI
|
||||||
- uvicorn
|
- uvicorn
|
||||||
- websockets
|
- websockets
|
||||||
|
- asyncpg (PostgreSQL async driver)
|
||||||
|
- PostgreSQL database
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- anime.js (animations)
|
- anime.js (animations)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user