Skip to content

batikanor/mx-egg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 

Repository files navigation

Tactical Football 2D

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.

Features

  • 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

Getting Started

Prerequisites

  • Node.js 18+
  • npm or yarn

Installation

npm install

Development

Run the development server:

npm run dev

Open http://localhost:3000 in your browser.

Build

Build for production:

npm run build

Run production build locally:

npm start

Deployment to Vercel

Method 1: Deploy via Vercel CLI

  1. Install Vercel CLI globally:
npm install -g vercel
  1. Deploy from your project directory:
vercel
  1. Follow the prompts to link your project

  2. For production deployment:

vercel --prod

Method 2: Deploy via Vercel Dashboard

  1. Push your code to GitHub:
git push origin main
  1. Go to vercel.com and sign in

  2. Click "New Project"

  3. Import your GitHub repository

  4. Vercel will auto-detect Next.js settings

  5. Click "Deploy"

Your app will be live in minutes!

Method 3: Deploy via GitHub Integration

  1. Connect your GitHub account to Vercel

  2. Push code to your repository

  3. Vercel will automatically deploy on every push to main

Project Structure

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

How to Play

  1. The game starts automatically with both teams positioned on the field
  2. Click any sector on the tactical grid to assign a player to defend/attack that zone
  3. Assigned players (shown in amber) will move to and maintain position in their sector
  4. Use the speed slider to adjust simulation speed
  5. Pause/resume using the play/pause button
  6. Goals trigger automatic reset sequences

Tech Stack

  • Framework: Next.js 16
  • Language: TypeScript
  • Styling: Tailwind CSS 4
  • Icons: Lucide React
  • Runtime: React 19

Configuration

The game uses these configuration files:

  • vercel.json - Vercel deployment settings
  • next.config.ts - Next.js configuration
  • tailwind.config.ts - Tailwind CSS customization
  • tsconfig.json - TypeScript compiler options

Environment Variables

No environment variables required for basic deployment.

License

ISC


AI Player Knowledge System Architecture

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.

System Overview

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)

1. Trajectory Prediction System

1.1 Ball Physics Simulation

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
Loading

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

1.2 Why Physics-Based vs ML-Based Trajectory Prediction?

We considered using transformer models from Hugging Face for trajectory prediction but chose deterministic physics simulation instead. Here's why:

ML Approaches Considered

1. Trajectory Transformer (Hugging Face)

  • Models like huggingface/trajectory-transformer or 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

Why We Chose Algorithmic Physics Simulation

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

Current Implementation Benefits

// 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

When Would We Use ML Instead?

If the game environment becomes more complex, ML might become valuable:

Scenarios where ML would be better:

  1. Wind/weather effects: Non-deterministic environmental factors
  2. Player skill variation: Different players have different physics
  3. Irregular surfaces: Grass, mud, or varying friction zones
  4. Ball spin/curve: Magnus effect for realistic curved shots
  5. Multi-ball interactions: Complex collision dynamics
  6. Real-world video analysis: Predicting from camera footage

Migration Path: If we add these features, we could:

  1. Keep physics-based for basic prediction
  2. Add ML layer for complex effects
  3. Use ensemble: physics (fast) + ML (accurate for edge cases)

Inspirations from Research

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.


1.3 Interception Analysis Algorithm

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
Loading

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)

2. LLM Input/Output System

2.1 Exact Data Sent to Grok 4.1 Fast

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
Loading

2.2 Request Format Details

Model: x-ai/grok-4.1-fast

  • Why chosen: Fastest Grok model with vision support, ~2s latency
  • Alternatives considered:
    • grok-2-vision-1212: Older, slower
    • grok-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."
}

3. FOV Screenshot Capture System

3.1 Rotating Capture Mechanism

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
Loading

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)

3.2 Screenshot Quality Comparison

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

4. Knowledge Base Population Flow

4.1 Frame-by-Frame Updates (60 FPS)

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
Loading

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

4.2 Strategic Context (Static/Slow Updates)

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
Loading

5. AI Decision Integration Loop

5.1 5-Second Interval Cycle

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
Loading

5.2 Non-Blocking Pattern Details

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 setInterval capture state at creation time
  • Symptom: FOV count always showed 0 even with 5 screenshots
  • Solution: Ref always points to current state, bypassing closure staleness

6. Complete Data Flow Architecture

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
Loading

7. Performance Characteristics

7.1 Update Frequencies

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

7.2 Memory Footprint

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

7.3 Network Usage (AI Calls)

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

8. Why This Architecture?

8.1 Design Decisions

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

8.2 Alternative Approaches Considered

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

8.3 Key Innovations

  1. Trajectory-Based Prediction: AI sees the future, not just the present
  2. Rotating Screenshot Capture: Distributes CPU load evenly
  3. Multimodal LLM Integration: Combines vision + physics data
  4. Non-Blocking Architecture: 60 FPS gameplay with AI running
  5. Append-Only Decision Log: Full transparency of AI reasoning

9. Algorithm Laboratory (Jetbrains Challenge)

Overview

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.

Access

Navigate to /algorithm-lab or click the algorithm indicator (🧪) in the game controls.

Features

1. Custom Algorithm Editor

  • 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

2. Algorithm Types Supported

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

3. Benchmarking System

  • 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

4. Game Integration

  • Algorithms automatically activate upon submission
  • Persistent storage via localStorage
  • Visual indicator in game UI showing active algorithm
  • One-click reset to default physics

Research Applications

Example Use Cases:

  1. Test if quadratic friction (air resistance) improves prediction accuracy for high-speed shots
  2. Explore how surface friction zones affect player positioning strategies
  3. Determine optimal time step for balancing accuracy vs performance
  4. Evaluate whether environmental factors (wind) create more dynamic gameplay
  5. Find the bounce energy loss coefficient that produces most realistic physics

Sample Algorithm

{
  "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
  }
}

Workflow

  1. Design → Edit JSON configuration in the lab
  2. Validate → Check for errors and view documentation
  3. Benchmark → Run accuracy tests vs baseline
  4. Deploy → Submit to activate in game
  5. Observe → Play matches and track scores
  6. Iterate → Refine based on performance data

Documentation

For complete technical documentation, see ALGORITHM_LAB.md


See Also

About

The new mx easter egg

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors