docs(02-01): complete paddle2 and AI opponent plan

- Create 02-01-SUMMARY.md with task commits, decisions, and dependency graph
- Update STATE.md: position at 02-02, add AI/difficulty decisions to context
- Update ROADMAP.md: Phase 2 in progress (1/2 plans)
- Mark REQUIREMENTS.md complete: CORE-08, AI-01, AI-02, AI-03, AI-04

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dabit
2026-03-10 21:08:12 +01:00
parent 398bc4a20e
commit 836cb01ec8
4 changed files with 139 additions and 24 deletions

View File

@@ -0,0 +1,106 @@
---
phase: 02-core-gameplay
plan: 01
subsystem: gameplay
tags: [canvas, vanilla-js, ai, physics, input, pong]
# Dependency graph
requires:
- phase: 01-foundation
provides: GameConfig, GameState.paddle1, GameState.ball, Physics (update/collide/serve), Input (W/S), Renderer, GameLoop
provides:
- GameState.paddle2 object (mirrors paddle1 structure, positioned on right side)
- GameConfig.WIN_SCORE and AI_EASY/AI_MEDIUM/AI_HARD difficulty presets
- Input.getVerticalInput2() for ArrowUp/ArrowDown keys
- Input.cleanup() with named handler refs for event listener removal
- AI object with predictive ray-cast interception (wall-bounce simulation)
- Physics._checkPaddle2Collision() with 5-zone deflection sending ball LEFT
- Physics.update() extended with paddle2 movement branch (mode: '2p' or 'ai')
- GameState.score1, score2, mode, difficulty, gameState, winner fields
affects: [02-02, 02-core-gameplay]
# Tech tracking
tech-stack:
added: []
patterns:
- "AI module as plain object with init/update/_predictBallY — same module-object pattern as Physics/Input"
- "Predictive ray-cast AI: steps through future positions with wall bounces instead of linear interpolation"
- "Difficulty preset as config object: { speed, reactionDelay, errorMargin } — all tunable from GameConfig"
- "Named event handler refs on Input (_handleKeyDown/_handleKeyUp) enable cleanup() for memory management"
key-files:
created: []
modified:
- index.html
key-decisions:
- "AI waits when ball moves toward player (vx < 0), reacts only when ball approaches (vx > 0) — avoids premature repositioning"
- "Deadzone of 3px in AI movement prevents micro-jitter when paddle center is already near target"
- "Physics.onResize() now calls this.init() to fully reposition both paddles on window resize"
- "Paddle2 collision pushes ball.x = paddle.x - ball.radius - 1 (mirrored from paddle1's right-push)"
patterns-established:
- "Mode guard in Physics.update(): GameState.mode === '2p' branches to human input, 'ai' branches to AI.update()"
- "Collision guards: ball.vx < 0 for paddle1, ball.vx > 0 for paddle2 — prevents wrong-direction collision checks"
requirements-completed: [CORE-08, AI-01, AI-02, AI-03, AI-04]
# Metrics
duration: 2min
completed: 2026-03-10
---
# Phase 2 Plan 01: Paddle2 and AI Opponent Summary
**GameState.paddle2 + full AI opponent module with predictive ray-cast interception and three tunable difficulty levels (easy/medium/hard)**
## Performance
- **Duration:** ~2 min
- **Started:** 2026-03-10T20:04:26Z
- **Completed:** 2026-03-10T20:06:12Z
- **Tasks:** 2
- **Files modified:** 1
## Accomplishments
- Added paddle2 to GameState (mirrors paddle1 structure) and positioned it on right side in Physics.init()
- Extended Input with ArrowUp/ArrowDown support, named handler refs, and cleanup() method
- Added AI module with predictive ray-cast (_predictBallY simulates wall bounces) plus reaction delay and error margin per difficulty
- Added Physics._checkPaddle2Collision() using same 5-zone deflection as paddle1 but sends ball leftward
- Extended Physics.update() with paddle2 movement branch (2p human or ai mode)
## Task Commits
Each task was committed atomically:
1. **Task 1: Extend GameConfig, GameState, and Input for two-player support** - `a2f0bc3` (feat)
2. **Task 2: Add AI module and extend Physics for paddle2 movement and collision** - `398bc4a` (feat)
## Files Created/Modified
- `/home/dabit/projects/gijs/index.html` - Added WIN_SCORE/AI presets to GameConfig; paddle2/score/mode/difficulty/gameState/winner to GameState; refactored Input with named handlers + getVerticalInput2(); extended Physics.init() and onResize(); added _checkPaddle2Collision(); extended Physics.update() for paddle2; added AI object; added AI.init() to init block
## Decisions Made
- Physics.onResize() now calls this.init() instead of just updating width/height, so paddle2 gets repositioned on resize
- AI reaction timer resets to 0 whenever ball.vx < 0 (ball moving toward player) — AI fully waits before reacting again once ball returns
- Deadzone of 3px prevents AI paddle jitter when already near target
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
None.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- paddle2 exists with valid x/y/width/height/speed/color — ready for Renderer to draw it in Plan 02-02
- AI.update() fires when GameState.mode === 'ai' — mode selection UI needed (Plan 02-02)
- Score tracking fields (score1, score2) and gameState state machine fields are wired and ready for Plan 02-02 scoring logic
---
*Phase: 02-core-gameplay*
*Completed: 2026-03-10*