A real-time tactical football simulation game built with Next.js, React, and TypeScript. Control your team's positioning using a 3x3 tactical grid while the AI controls all player movements.
- Real-time physics simulation
- Tactical command system with 9-sector grid
- Dynamic player positioning and AI
- Score tracking and match states
- Adjustable simulation speed
- Pause/resume functionality
- Responsive design with Tailwind CSS
- Node.js 18+
- npm or yarn
npm installRun the development server:
npm run devOpen http://localhost:3000 in your browser.
Build for production:
npm run buildRun production build locally:
npm start- Install Vercel CLI globally:
npm install -g vercel- Deploy from your project directory:
vercel-
Follow the prompts to link your project
-
For production deployment:
vercel --prod- Push your code to GitHub:
git push origin main-
Go to vercel.com and sign in
-
Click "New Project"
-
Import your GitHub repository
-
Vercel will auto-detect Next.js settings
-
Click "Deploy"
Your app will be live in minutes!
-
Connect your GitHub account to Vercel
-
Push code to your repository
-
Vercel will automatically deploy on every push to main
football-2d/
├── app/
│ ├── globals.css # Global styles with Tailwind
│ ├── layout.tsx # Root layout
│ └── page.tsx # Home page
├── components/
│ └── TacticalFootball.tsx # Main game component
├── public/ # Static assets
├── next.config.ts # Next.js configuration
├── tailwind.config.ts # Tailwind configuration
├── tsconfig.json # TypeScript configuration
└── vercel.json # Vercel deployment configuration
- The game starts automatically with both teams positioned on the field
- Click any sector on the tactical grid to assign a player to defend/attack that zone
- Assigned players (shown in amber) will move to and maintain position in their sector
- Use the speed slider to adjust simulation speed
- Pause/resume using the play/pause button
- Goals trigger automatic reset sequences
- Framework: Next.js 16
- Language: TypeScript
- Styling: Tailwind CSS 4
- Icons: Lucide React
- Runtime: React 19
The game uses these configuration files:
vercel.json- Vercel deployment settingsnext.config.ts- Next.js configurationtailwind.config.ts- Tailwind CSS customizationtsconfig.json- TypeScript compiler options
No environment variables required for basic deployment.
ISC
This section explains how each AI player builds and maintains their knowledge base, combining visual perception, physics predictions, and strategic reasoning to make intelligent tactical decisions.
Each of the 8 players (4 red, 4 blue) maintains a comprehensive PlayerKnowledge object updated at different frequencies:
- Physics predictions: 60 FPS (every frame)
- Visual snapshots: Every 500ms per player (4-second cycle for all 8 players)
- AI decisions: Every 5 seconds (random player selection)
graph TD
A[Current Ball State<br/>x, y, vx, vy] --> B[Simulation Loop]
B --> C[Apply Friction<br/>vx *= 0.94<br/>vy *= 0.94]
C --> D[Update Position<br/>x += vx<br/>y += vy]
D --> E{Hit Wall?}
E -->|Yes| F[Bounce<br/>vx *= -0.8<br/>Energy Loss]
E -->|No| G[Continue]
F --> G
G --> H[Store Point<br/>x, y, t, velocity]
H --> I{Ball Stopped?<br/>speed < 0.5}
I -->|No| J{Max Time?<br/>t < 3.0s}
J -->|No| B
I -->|Yes| K[Landing Position]
J -->|Yes| K
K --> L[Return Trajectory<br/>Array of Points]
style C fill:#ffd,stroke:#333,stroke-width:2px
style F fill:#faa,stroke:#333,stroke-width:2px
style K fill:#afa,stroke:#333,stroke-width:2px
Why this approach?
- Friction = 0.94: Matches actual game physics exactly
- 0.1s time steps: Balance between accuracy and computation
- Wall bounce with energy loss: Realistic physics, reduces infinite bouncing
- Stop threshold = 0.5: Ball considered stopped when almost motionless
We considered using transformer models from Hugging Face for trajectory prediction but chose deterministic physics simulation instead. Here's why:
1. Trajectory Transformer (Hugging Face)
- Models like
huggingface/trajectory-transformeror custom sequence prediction transformers - Inspired by papers on sports trajectory prediction (e.g., "TrajectoryNet: An Embedded GPS Trajectory Representation")
- Would learn ball physics from training data
2. LSTM/GRU Sequence Models
- Recurrent neural networks for time-series prediction
- Could model complex physics interactions
- Popular in sports analytics
3. Physics-Informed Neural Networks (PINNs)
- Hybrid approach combining ML with physics equations
- More interpretable than pure ML
| Factor | Physics-Based (Our Choice) | ML-Based (Rejected) |
|---|---|---|
| Accuracy | 100% (same physics as game) | 85-95% (learned approximation) |
| Determinism | Always consistent | May vary slightly |
| Latency | <0.1ms per prediction | ~5-20ms inference |
| Dependencies | Zero (pure TypeScript) | Heavy (~100MB+ models) |
| Training Data | None needed | Need 10K+ trajectory examples |
| Memory | Negligible | 50-200MB model weights |
| Browser Compatibility | Perfect | Requires WASM/GPU support |
| Debug-ability | Easy to trace | Black box |
// Simple, fast, and 100% accurate
const BASE_FRICTION = 0.94; // Matches game physics exactly
while (t < predictionTime) {
vx *= BASE_FRICTION; // Same calculation as game
vy *= BASE_FRICTION;
x += vx;
y += vy;
// Perfect prediction because we use identical physics
}Key Advantages:
- ✅ Zero inference latency: Runs in <0.1ms vs ~5-20ms for ML models
- ✅ Perfect accuracy: Uses exact same friction coefficient (0.94) as game engine
- ✅ No dependencies: Pure TypeScript, no model files to load
- ✅ Deterministic: Same inputs always produce same outputs
- ✅ Easy to debug: Can trace every calculation step
- ✅ Lightweight: <5KB code vs >100MB for ML models
If the game environment becomes more complex, ML might become valuable:
Scenarios where ML would be better:
- Wind/weather effects: Non-deterministic environmental factors
- Player skill variation: Different players have different physics
- Irregular surfaces: Grass, mud, or varying friction zones
- Ball spin/curve: Magnus effect for realistic curved shots
- Multi-ball interactions: Complex collision dynamics
- Real-world video analysis: Predicting from camera footage
Migration Path: If we add these features, we could:
- Keep physics-based for basic prediction
- Add ML layer for complex effects
- Use ensemble: physics (fast) + ML (accurate for edge cases)
While we didn't use transformers, we were inspired by:
- TrajectoryNet (GPS trajectory embedding): Idea of encoding movement patterns
- SoccerNet (sports video understanding): Multimodal approach (vision + data)
- Graph Neural Networks for Sports: Modeling player interactions as graphs
- Physics-Informed Deep Learning: Combining domain knowledge with ML
Our hybrid approach: We use the multimodal concept from ML research (vision + structured data sent to Grok 4.1) but keep trajectory prediction deterministic for speed and accuracy.
graph LR
A[Player Position<br/>x, y] --> B[Ball Trajectory<br/>Points Array]
B --> C{For Each Point<br/>in Trajectory}
C --> D[Calculate Distance<br/>from Player to Point]
C --> E[Get Time Available<br/>point.t]
D --> F[Required Speed<br/>= distance / time]
E --> F
F --> G{Speed ≤ Player Max?<br/>AND distance > 15px}
G -->|Yes| H[INTERCEPT POSSIBLE]
G -->|No| C
H --> I[Calculate Confidence<br/>1 - requiredSpeed/maxSpeed]
I --> J[Return:<br/>✓ Intercept Point<br/>✓ Time to Reach<br/>✓ Confidence 0-1]
C --> K{Already at Ball?<br/>distance ≤ 15px}
K -->|Yes| L[Instant Intercept<br/>time = 0, conf = 1.0]
K -->|No| M{No More Points?}
M -->|Yes| N[CANNOT INTERCEPT<br/>null]
style H fill:#afa,stroke:#333,stroke-width:2px
style N fill:#faa,stroke:#333,stroke-width:2px
style L fill:#9f9,stroke:#333,stroke-width:2px
Algorithm Details:
- INTERCEPT_RADIUS = 15px: Player can "touch" ball within this distance
- Confidence score: Higher when player has comfortable speed margin
- Early exit: Returns first viable intercept point (earliest opportunity)
graph TD
A[PlayerKnowledge Object] --> B[Text Context Builder]
A --> C[Image Array Reverser]
B --> D[Team & Role<br/>Red/Blue, GK/FIELD]
B --> E[Game Score<br/>red: 2, blue: 1]
B --> F[Current Strategy<br/>⚡ Ball Pressure]
B --> G[Goal Positions<br/>My: left, Opp: right]
B --> H[Ball Prediction<br/>Landing: x, y<br/>Time: 2.3s]
B --> I[Can I Intercept?<br/>Yes in 1.5s / No]
B --> J[Teammates Can Intercept<br/>Count: 2]
B --> K[Opponent Threats<br/>Count: 1]
B --> L[Teammate Strategies<br/>r0: 🛡️ Defensive Cover<br/>r1: ⚡ Ball Pressure<br/>r2: 🔄 Box-to-Box]
B --> M[Available Strategies<br/>13 total:<br/>6 offensive<br/>4 defensive<br/>3 balanced]
B --> N[Role Guidelines<br/>GK: Stay near goal...<br/>FIELD: Chase ball...]
B --> O[Situational Advice<br/>Winning: Defend<br/>Losing: Attack<br/>Tied: Balanced]
C --> P[Screenshot 5<br/>Most Recent]
C --> Q[Screenshot 4<br/>1s ago]
C --> R[Screenshot 3<br/>2s ago]
C --> S[Screenshot 2<br/>3s ago]
C --> T[Screenshot 1<br/>4s ago]
D --> U[JSON Text Prompt<br/>~800 words]
E --> U
F --> U
G --> U
H --> U
I --> U
J --> U
K --> U
L --> U
M --> U
N --> U
O --> U
U --> V[OpenRouter API Request]
P --> V
Q --> V
R --> V
S --> V
T --> V
V --> W[Grok 4.1 Fast<br/>Multimodal LLM]
W --> X[JSON Response<br/>200-300 bytes]
X --> Y[selectedStrategy<br/>🛡️ Defensive Cover]
X --> Z[reasoning<br/>Ball near our goal,<br/>I should protect it.]
style V fill:#bbf,stroke:#333,stroke-width:2px
style W fill:#f9f,stroke:#333,stroke-width:3px
style X fill:#afa,stroke:#333,stroke-width:2px
Model: x-ai/grok-4.1-fast
- Why chosen: Fastest Grok model with vision support, ~2s latency
- Alternatives considered:
grok-2-vision-1212: Older, slowergrok-4-fast: No number in version (deprecated naming)
Content Array Structure:
[
{
type: "text",
text: "You are an AI football player...\n[800 words of context]"
},
{ type: "image_url", url: "data:image/jpeg;base64,..." }, // Screenshot 5 (newest)
{ type: "image_url", url: "data:image/jpeg;base64,..." }, // Screenshot 4
{ type: "image_url", url: "data:image/jpeg;base64,..." }, // Screenshot 3
{ type: "image_url", url: "data:image/jpeg;base64,..." }, // Screenshot 2
{ type: "image_url", url: "data:image/jpeg;base64,..." } // Screenshot 1 (oldest)
]Response Format:
{
"selectedStrategy": "🛡️ Defensive Cover",
"reasoning": "The ball is near our goal and an opponent is approaching. I need to position myself between the ball and our goal to block any shot attempts. My teammates are already pressuring upfield."
}sequenceDiagram
participant T as Timer<br/>(500ms)
participant I as Index Counter<br/>(0-7)
participant R as React State<br/>setBackgroundCapture
participant C as Canvas<br/>PlayerPOV Component
participant E as Encoder<br/>toDataURL
participant S as Storage<br/>PlayerKnowledge Map
Note over T,S: Cycle starts with player 0
T->>I: Tick #1
I->>I: index = 0
I->>R: setBackgroundCapturePlayer('r0')
R->>C: Render off-screen at 320×180px
Note over C: Draws field, players, ball<br/>from r0's first-person view
C->>C: Wait 100ms for render
C->>E: canvas.toDataURL('image/jpeg', 0.3)
Note over E: JPEG encoding<br/>~10-30ms CPU time
E->>S: Add to r0.fovScreenshots[]
S->>S: Keep last 5, shift if > 5
Note over T,S: 500ms passes...
T->>I: Tick #2
I->>I: index = 1
I->>R: setBackgroundCapturePlayer('r1')
Note over T,S: Repeat for all 8 players...
T->>I: Tick #9 (after 4 seconds)
I->>I: index = 0 (wrap around)
I->>R: setBackgroundCapturePlayer('r0')
Note over T,S: Each player gets fresh screenshot<br/>every 4 seconds
Performance Optimizations:
| Setting | Value | Why? |
|---|---|---|
| Interval | 500ms | Only 1 capture at a time, no GPU overload |
| JPEG Quality | 0.3 | AI doesn't need high-res, 70% smaller files |
| Resolution | 320×180px | 1/6 scale of 1920×1080, fast to render |
| Render Delay | 100ms | Let canvas fully draw before encoding |
| Buffer Size | 5 images | 20 seconds of history (5 × 4s interval) |
Before optimization (0.7 quality, 125ms interval):
- File size: ~50 KB per image
- Encoding time: ~40ms (blocks main thread)
- Captures per second: 8 (all players simultaneously)
- Result: Visible lag spikes every second
After optimization (0.3 quality, 500ms interval):
- File size: ~15 KB per image
- Encoding time: ~12ms (blocks briefly)
- Captures per second: 2 (staggered across players)
- Result: Smooth 60 FPS gameplay
graph TD
A[Game Loop Tick<br/>~16.6ms budget] --> B[Update Physics]
B --> C[Move Ball<br/>Apply friction]
B --> D[Move Players<br/>8 players × physics]
C --> E[Predict Ball Trajectory<br/>3 seconds ahead]
D --> F[For Each Player:<br/>Predict Their Path]
E --> G[For Each Player]
F --> G
G --> H[analyzeInterception<br/>Can player reach ball?]
H --> I{Update PlayerKnowledge}
I --> J[ballPrediction<br/>landing, time]
I --> K[myTrajectory<br/>my predicted path]
I --> L[teammatePredictions<br/>7 others analyzed]
I --> M[opponentPredictions<br/>4 opponents analyzed]
I --> N[canInterceptBall<br/>boolean flag]
I --> O[myInterceptPoint<br/>x, y or null]
I --> P[timeToInterceptBall<br/>seconds or null]
J --> Q[React State Update<br/>setPlayerKnowledge]
K --> Q
L --> Q
M --> Q
N --> Q
O --> Q
P --> Q
Q --> R[Triggers Re-render<br/>UI updates]
style E fill:#ffd,stroke:#333,stroke-width:2px
style H fill:#ddf,stroke:#333,stroke-width:2px
style Q fill:#f9f,stroke:#333,stroke-width:2px
Why per-frame updates?
- Ball and player positions change every frame
- Trajectory predictions must stay current
- Interception opportunities can appear/disappear quickly
- AI needs real-time data for good decisions
graph LR
A[Game Start] --> B[Initialize PlayerKnowledge]
B --> C[Static Data<br/>Never Changes]
B --> D[Game State<br/>Changes per frame]
B --> E[AI State<br/>Changes per decision]
C --> F[roleDescriptions<br/>GK vs FIELD guidelines]
C --> G[strategyGuidelines<br/>13 available strategies]
C --> H[whenToUseStrategies<br/>win/lose/tie advice]
C --> I[myGoalSide<br/>left or right]
C --> J[opponentGoalSide<br/>opposite side]
D --> K[currentScore<br/>red: X, blue: Y]
D --> L[team<br/>red or blue]
E --> M[myCurrentStrategy<br/>Updated by AI every ~5s]
E --> N[teammateStrategies<br/>Updated when teammates change]
E --> O[strategyThoughts<br/>Append-only log of AI decisions]
style C fill:#efe,stroke:#333,stroke-width:2px
style D fill:#ffe,stroke:#333,stroke-width:2px
style E fill:#eef,stroke:#333,stroke-width:2px
graph TD
A[AI Interval Timer<br/>Tick every 5s] --> B[Select Random Player<br/>1 of 8]
B --> C{Get PlayerKnowledge<br/>from Ref NOT State}
C --> D{Has FOV Screenshots?<br/>length > 0}
D -->|No| E[⏭️ Skip<br/>Wait for screenshots]
E --> F[Return to Timer]
D -->|Yes| G[🚀 Fire IIFE<br/>Don't await!]
G --> H[Async Block Starts]
G --> I[Continue Interval<br/>Game keeps running]
H --> J[Fetch /api/select-strategy<br/>POST with knowledge + images]
J --> K[~2 seconds pass...]
K --> L{Response OK?}
L -->|No| M[❌ Log Error<br/>Show status, details]
M --> N[End Async Block]
L -->|Yes| O[✅ Parse JSON Response]
O --> P[Extract:<br/>selectedStrategy<br/>reasoning]
P --> Q[setPlayerKnowledge<br/>Update state]
Q --> R[Update My Strategy]
Q --> S[Add to strategyThoughts]
Q --> T[Notify Teammates<br/>Update their teammateStrategies]
R --> U[UI Displays Change<br/>Strategy panel updates]
S --> V[AI Thoughts Log<br/>New entry appears]
T --> W[Other Players See<br/>Updated teammate info]
style G fill:#afa,stroke:#333,stroke-width:2px
style I fill:#afa,stroke:#333,stroke-width:2px
style M fill:#faa,stroke:#333,stroke-width:2px
style Q fill:#f9f,stroke:#333,stroke-width:2px
The Problem: If we await the API call in the interval callback, the game would freeze for ~2 seconds every 5 seconds.
The Solution: IIFE (Immediately Invoked Function Expression)
setInterval(() => {
// This callback is NOT async
const knowledge = playerKnowledgeRef.current.get(playerId);
// Fire and forget!
(async () => {
const response = await fetch('/api/select-strategy', ...);
const data = await response.json();
setPlayerKnowledge(...); // Updates happen later, whenever ready
})(); // <-- Immediately invoked, not awaited
// Interval continues immediately without blocking
}, 5000);Why use a Ref instead of State?
const playerKnowledgeRef = useRef(playerKnowledge);
useEffect(() => {
playerKnowledgeRef.current = playerKnowledge;
}, [playerKnowledge]);- Problem: Closures in
setIntervalcapture state at creation time - Symptom: FOV count always showed 0 even with 5 screenshots
- Solution: Ref always points to current state, bypassing closure staleness
graph TB
subgraph "60 FPS Game Loop"
A[Physics Engine] --> B[Ball Movement]
A --> C[Player Movement]
end
subgraph "Prediction Layer"
B --> D[predictBallTrajectory<br/>3s lookahead]
C --> E[predictPlayerTrajectory<br/>2s lookahead]
D --> F[analyzeInterception<br/>Can reach ball?]
E --> F
end
subgraph "Visual Perception"
G[500ms Timer] --> H[Rotate Player Index<br/>0→1→2→...→7]
H --> I[Render POV Canvas<br/>320×180px]
I --> J[JPEG Encode 0.3<br/>~15KB per image]
J --> K[Store Last 5<br/>Per player]
end
subgraph "Knowledge Base"
F --> L[PlayerKnowledge Map<br/>8 players]
K --> L
M[Game State<br/>Score, Team] --> L
N[Strategic Context<br/>Roles, Guidelines] --> L
end
subgraph "AI Decision System"
O[5s Random Interval] --> P{Has Screenshots?}
L --> P
P -->|Yes| Q[Build Multimodal<br/>Text + 5 Images]
Q --> R[Grok 4.1 Fast<br/>~2s latency]
R --> S[Strategy + Reasoning]
S --> L
end
subgraph "User Interface"
L --> T[Strategy Panel<br/>Show strategies]
L --> U[POV View<br/>Show screenshots]
L --> V[AI Thoughts Log<br/>Show reasoning]
end
style D fill:#ffd,stroke:#333,stroke-width:2px
style J fill:#dff,stroke:#333,stroke-width:2px
style L fill:#f9f,stroke:#333,stroke-width:3px
style R fill:#fdf,stroke:#333,stroke-width:2px
| Component | Frequency | CPU Usage | Why This Rate? |
|---|---|---|---|
| Physics | 60 FPS | 5-10% | Match display refresh, smooth motion |
| Trajectory Predictions | 60 FPS | 3-5% | Physics changes every frame |
| FOV Screenshots | 0.125 FPS per player (8s cycle) |
2-5% spikes | Balance freshness vs CPU load |
| AI Decisions | 0.2 FPS random (5s interval) |
0% (network I/O) | Account for ~2s LLM latency |
| Data Type | Size | Count | Total |
|---|---|---|---|
| Screenshots (15 KB × 5) | 75 KB | ×8 players | 600 KB |
| Trajectory Points (40 bytes × 30) | 1.2 KB | ×16 entities | 19 KB |
| Strategic Data (strings) | ~5 KB | ×8 players | 40 KB |
| AI Thoughts History | ~200 bytes | ×20 decisions | 4 KB |
| Total | ~663 KB |
| Metric | Value | Calculation |
|---|---|---|
| Request Size | ~500 KB | 5 images (15 KB) + text (25 KB) |
| Response Size | ~1 KB | JSON with strategy + reasoning |
| Calls per Minute | 12 | 60s ÷ 5s interval |
| Bandwidth | ~6 MB/min | 12 × 500 KB |
| Monthly Cost (estimate) | $5-10 | Depends on usage, Grok pricing |
1. Multiple Small Diagrams vs One Big Diagram
- ✅ Easier to understand specific subsystems
- ✅ Can reference individual diagrams in code comments
- ✅ Better for documentation and onboarding
- ❌ Need to understand how diagrams connect
2. Ref for PlayerKnowledge Access
- ✅ Solves closure staleness in intervals
- ✅ Always accesses current state
- ✅ No dependency array issues
- ❌ More React concepts to understand
3. Fire-and-Forget AI Calls
- ✅ Game never freezes
- ✅ Multiple requests can be in-flight
- ✅ Responses update UI asynchronously
- ❌ Can't guarantee request ordering
4. JPEG 0.3 Quality
- ✅ 70% smaller files than 0.7
- ✅ Faster encoding (less CPU)
- ✅ AI can still see game clearly
- ❌ Lower visual quality for humans
5. 500ms Screenshot Interval
- ✅ Only 1 canvas rendered at a time
- ✅ Smooth gameplay maintained
- ✅ Still get 5 screenshots per 20s
- ❌ Slower refresh than 60 FPS video
| Approach | Why Not Used |
|---|---|
| Video streaming to AI | Too much bandwidth, complex encoding |
| Screenshot all players simultaneously | GPU overload, caused lag |
| Higher quality images | No benefit for AI, just slower |
| Synchronous AI calls | Would freeze game for 2+ seconds |
| Server-side rendering | Edge runtime limitations, latency |
| Simpler physics (no trajectories) | AI would be reactive, not proactive |
- Trajectory-Based Prediction: AI sees the future, not just the present
- Rotating Screenshot Capture: Distributes CPU load evenly
- Multimodal LLM Integration: Combines vision + physics data
- Non-Blocking Architecture: 60 FPS gameplay with AI running
- Append-Only Decision Log: Full transparency of AI reasoning
The Algorithm Lab is a dedicated interface for sports science researchers to design, test, and benchmark custom trajectory prediction algorithms. This feature enables experimentation with different physics models to determine which yields optimal game performance.
Navigate to /algorithm-lab or click the algorithm indicator (🧪) in the game controls.
- JSON-based configuration: Define algorithms using a structured schema
- Live validation: Immediate feedback on configuration errors
- Sample templates: Pre-loaded examples including enhanced friction models
- Built-in documentation: Complete schema reference
Custom Friction Models:
- Linear:
friction = c[0] + c[1] * velocity - Quadratic (air resistance):
friction = c[0] + c[1] * velocity^2 - Exponential:
friction = c[0] * exp(-c[1] * velocity) - Piecewise: Different friction for velocity ranges
Environmental Factors:
- Wind effects (constant X/Y velocity components)
- Surface friction zones (define field areas with modified friction)
- Custom bounce energy loss coefficients
Ball Physics:
- Mass modifications
- Radius adjustments
- Spin decay rates
- Compare custom algorithms against baseline physics
- Metrics tracked:
- Average prediction error (pixels)
- Maximum error (pixels)
- Computation time (milliseconds)
- Test cases passed
- Visual results table with performance indicators
- Algorithms automatically activate upon submission
- Persistent storage via localStorage
- Visual indicator in game UI showing active algorithm
- One-click reset to default physics
Example Use Cases:
- Test if quadratic friction (air resistance) improves prediction accuracy for high-speed shots
- Explore how surface friction zones affect player positioning strategies
- Determine optimal time step for balancing accuracy vs performance
- Evaluate whether environmental factors (wind) create more dynamic gameplay
- Find the bounce energy loss coefficient that produces most realistic physics
{
"name": "Enhanced Friction Model",
"version": "1.0.0",
"author": "Sports Science Lab",
"description": "Quadratic friction with surface zones",
"parameters": {
"friction": 0.92,
"timeStep": 0.05,
"maxPredictionTime": 4.0,
"bounceEnergyLoss": 0.75,
"stopThreshold": 0.3
},
"customFriction": {
"type": "quadratic",
"coefficients": [0.92, 0.001],
"description": "Air resistance increases with speed"
},
"environment": {
"windEnabled": false,
"surfaceFrictionZones": [{
"x1": 0, "y1": 0, "x2": 200, "y2": 600,
"frictionMultiplier": 1.1
}]
},
"metadata": {
"dateCreated": "2025-11-23",
"tags": ["realistic", "quadratic-friction"],
"expectedAccuracy": 92
}
}- Design → Edit JSON configuration in the lab
- Validate → Check for errors and view documentation
- Benchmark → Run accuracy tests vs baseline
- Deploy → Submit to activate in game
- Observe → Play matches and track scores
- Iterate → Refine based on performance data
For complete technical documentation, see ALGORITHM_LAB.md
- AI Setup Guide - How to configure OpenRouter API
- Trajectory Prediction Code - Physics simulation implementation
- API Route - LLM integration endpoint
- Algorithm Lab Documentation - Complete guide for sports science researchers