← Back to all posts

ballgorithm: math vs math. cursed ping pong that almost never ends

MathJune 20, 20258 min read
ballgorithm: math vs math. cursed ping pong that almost never ends

TL;DR: AI vs AI pong built in ~80 LOC. Fully autonomous. Two bots that never miss, rally forever. Math-powered chaos, non-deterministic. Code is a mess. Game still runs.

accidental weekend build

threw it together on impulse. didn't mean to. didn't scope it. started with ball trajectory, ended up with two bots fighting forever. no keyboard, no player. just math.

blue vs red. both controlled by code. once a rally starts, it rarely stops. you're watching two functions bounce a pixel forever.

game architecture

  • single <canvas>
  • no libraries, no physics engine
  • no game framework
  • ~80 lines of JavaScript

no time-based updates, no deltaTime. everything is frame-driven, pure math.

core math breakdown

1. Ball movement

b.x += b.dx;
b.y += b.dy;

Standard 2D kinematics. Ball moves at constant velocity vector (dx, dy).

2. Wall bounce

if (b.y <= b.r) {
  b.y = b.r;
  b.dy = Math.abs(b.dy);
} else if (b.y >= h - b.r) {
  b.y = h - b.r;
  b.dy = -Math.abs(b.dy);
}

Perfect vertical reflection. No energy loss. Ensures ball stays within canvas.

3. Prediction: where will ball hit paddle?

while (Math.abs(s.x - targetX) > Math.abs(s.dx) && bounces < max) {
  let ts = Math.min(1, Math.abs(targetX - s.x) / Math.abs(s.dx));
  s.x += s.dx * ts;
  s.y += s.dy * ts;

  if (s.y <= 0 || s.y >= h) s.dy *= -1;
}

Predicts Y position of the ball when it reaches the paddle's x. Raytraces the ball's path including bounces.

4. Uncertainty injection

s.y += (Math.random() - 0.5) * uncertaintyFactor;

Adds error to prediction based on:

  • paddle's anticipation stat
  • distance to paddle
  • number of bounces

5. Paddle AI motion

momentum += acceleration;
momentum *= 0.85;
y += momentum;
  • Acceleration based on distance to targetY
  • Damped momentum
  • Simulates spring-like chase toward target

6. Paddle targeting logic

targetY = predictedY + skill-based noise + center bias

Noise is random and scaled by:

  • errorRate
  • hesitation
  • confidence stat

7. Angle of return (deflection)

let hit = (ball.y - (p.y + p.h/2)) / (p.h/2);
let angle = hit * (0.3 + rallyBonus) + chaos;
ball.dy += angle;

If paddle hits ball near center: minimal curve. Off-center = more angle. Added chaos after long rallies.

8. Paddle personalities

{
  aggression: 0.7 to 1.0,
  anticipation: 0.5 to 1.0,
  errorRate: ~0.02,
  hesitation: ~0.1,
  confidence: 0.8 to 1.0 (live updated)
}
  • aggression: affects paddle speed
  • anticipation: affects prediction accuracy
  • errorRate: chance of bad prediction
  • hesitation: temporary delay
  • confidence: builds over hits, scales prediction precision

9. Rally dynamics

if (consecutiveHits > 2) {
  randomness = (Math.random() - 0.5) * consecutiveHits;
}

Tracks consecutive near-identical hits. Injects chaos to break loop. Sometimes works. Usually doesn't—AIs adapt.

10. Scoring

if (ball.x < 0) red wins;
if (ball.x > canvas.width) blue wins;
  • resets rally
  • drops confidence
  • shows popup

why rallies never end

  • prediction loop is near perfect
  • bots adapt each frame
  • error is injected, but confidence scales precision
  • rallies self-stabilize over time

Seen rallies cross 5000 hits.

cursed parts

  • all global vars: g, p1, b, x
  • no comments
  • theme switch is just CSS var toggle
  • update() and draw() do everything

closing thoughts

this wasn't meant to work, but it did. no planning, no tuning, just 2 AIs fighting each other using physics. no ML, no RL, just raw JS.

source lives in the page. view source.

🕹️ Play here → ballgorithm.b0a04gl.site

References

2D Physics and Ball Reflection

AI Paddle Prediction and Algorithms

Game Physics Programming

Bonus References