Bouncing balls in a heptagon · deepseek-chat
No runnable output Cost / run $0.0020 Runtime 24.8s
Prompt for this run
Show 20 numbered balls (1 to 20) dropping from the center of a heptagon that spins 360 degrees every 5 seconds. The balls are affected by gravity and friction, collide with each other, and bounce realistically off the rotating walls; show each ball's spin via the rotation of its number. Implement all collision detection yourself.
This one-shot run produced no runnable artifact.
agent produced no index.html
The transcript below shows what the model did — read it to see why the one shot fell short.
TAKE IT TO CODE
This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.
RUN
METRICS
Transcript · 2 messages
Show 20 numbered balls (1 to 20) dropping from the center of a heptagon that spins 360 degrees every 5 seconds. The balls are affected by gravity and friction, collide with each other, and bounce realistically off the rotating walls; show each ball's spin via the rotation of its number. Implement all collision detection yourself.
Here’s the complete `index.html` file with a spinning heptagon and 20 numbered balls that bounce realistically, including collision detection and ball rotation: html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Spinning Heptagon with Balls</title> <style> body { margin: 0; overflow: hidden; background: #111; } canvas { display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Constants const GRAVITY = 0.2; const FRICTION = 0.99; const RESTITUTION = 0.8; const BALL_RADIUS = 15; const HEPTAGON_RADIUS = Math.min(canvas.width, canvas.height) * 0.4; const HEPTAGON_SIDES = 7; const CENTER = { x: canvas.width / 2, y: canvas.height / 2 }; const BALL_COLORS = [ '#FF5733', '#33FF57', '#3357FF', '#FF33F5', '#33FFF5', '#F5FF33', '#A633FF', '#FFA633', '#33FFA6', '#FF33A6', '#33A6FF', '#A6FF33', '#FF3366', '#66FF33', '#3366FF', '#FF6633', '#33FF66', '#6633FF', '#FF33CC', '#33CCFF' ]; // Heptagon properties let heptagonAngle = 0; const heptagonRotationSpeed = (2 * Math.PI) / (5 * 60); // 360 degrees every 5 seconds // Generate heptagon vertices function getHeptagonVertices() { const vertices = []; for (let i = 0; i < HEPTAGON_SIDES; i++) { const angle = heptagonAngle + (i * 2 * Math.PI / HEPTAGON_SIDES); vertices.push({ x: CENTER.x + HEPTAGON_RADIUS * Math.cos(angle), y: CENTER.y + HEPTAGON_RADIUS * Math.sin(angle) }); } return vertices; } // Ball class class Ball { constructor(number) { this.number = number; this.x = CENTER.x; this.y = CENTER.y; this.vx = (Math.random() * 4 - 2); this.vy = (Math.random() * 4 - 2); this.radius = BALL_RADIUS; this.color = BALL_COLORS[number % BALL_COLORS.length]; this.rotation = 0; this.rotationSpeed = (Math.random() * 0.1 - 0.05); } update() { // Apply gravity this.vy += GRAVITY; // Apply friction this.vx *= FRICTION; this.vy *= FRICTION; // Update position this.x += this.vx; this.y += this.vy; // Update rotation this.rotation += this.rotationSpeed; // Bounce off walls (heptagon) const vertices = getHeptagonVertices(); for (let i = 0; i < vertices.length; i++) { const p1 = vertices[i]; const p2 = vertices[(i + 1) % vertices.length]; this.checkCollisionWithWall(p1, p2); } // Bounce off other balls for (let other of balls) { if (other !== this) { this.checkCollisionWithBall(other); } } } checkCollisionWithWall(p1, p2) { // Line segment collision detection const ballToLine = { x: this.x - p1.x, y: this.y - p1.y }; const lineDir = { x: p2.x - p1.x, y: p2.y - p1.y }; const lineLength = Math.hypot(lineDir.x, lineDir.y); const unitLineDir = { x: lineDir.x / lineLength, y: lineDir.y / lineLength }; // Project ball position onto line const projLength = ballToLine.x * unitLineDir.x + ballToLine.y * unitLineDir.y; // Closest point on line to ball let closestPoint; if (projLength < 0) { closestPoint = p1; } else if (projLength > lineLength) { closestPoint = p2; } else { closestPoint = { x: p1.x + projLength * unitLineDir.x, y: p1.y + projLength * unitLineDir.y }; } // Distance to closest point const distance = Math.hypot(this.x - closestPoint.x, this.y - closestPoint.y); if (distance < this.radius) { // Collision detected const normal = { x: this.x - closestPoint.x, y: this.y - closestPoint.y }; const norm = Math.hypot(normal.x, normal.y); normal.x /= norm; normal.y /= norm; // Reflect velocity const dotProduct = this.vx * normal.x + this.vy * normal.y; this.vx -= 2 * dotProduct * normal.x * RESTITUTION; this.vy -= 2 * dotProduct * normal.y * RESTITUTION; // Move ball out of collision const overlap = this.radius - distance; this.x += overlap * normal.x; this.y += overlap * normal.y; } } checkCollisionWithBall(other) { const dx = other.x - this.x; const dy = other.y - this.y; const distance = Math.hypot(dx, dy); const minDistance = this.radius + other.radius; if (distance < minDistance) { // Collision detected const nx = dx / distance; const ny = dy / distance; const p = 2 * (this.vx * nx + this.vy * ny - other.vx * nx - other.vy * ny) / (this.radius + other.radius); // Update velocities for elastic collision this.vx -= p * other.radius * nx * RESTITUTION; this.vy -= p * other.radius * ny * RESTITUTION; other.vx += p * this.radius * nx * RESTITUTION; other.vy += p * this.radius * ny * RESTITUTION; // Move balls apart to avoid overlap const overlap = (minDistance - distance) / 2; this.x -= overlap * nx; this.y -= overlap * ny; other.x += overlap * nx; other.y += overlap * ny; } } draw() { ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.rotation); // Draw ball ctx.beginPath(); ctx.arc(0, 0, this.radius, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); ctx.strokeStyle = '#fff'; ctx.stroke(); // Draw number ctx.fillStyle = '#fff'; ctx.font = `${BALL_RADIUS}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(this.number.toString(), 0, 0); ctx.restore(); } } // Create balls const balls = []; for (let i = 1; i <= 20; i++) { balls.push(new Ball(i)); } // Animation loop function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Update heptagon rotation heptagonAngle += heptagonRotationSpeed; if (heptagonAngle > 2 * Math.PI) heptagonAngle -= 2 * Math.PI; // Draw heptagon const vertices = getHeptagonVertices(); ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let i = 1; i < vertices.length; i++) { ctx.lineTo(vertices[i].x, vertices[i].y); } ctx.closePath(); ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke(); // Update and draw balls for (let ball of balls) { ball.update(); ball.draw(); } requestAnimationFrame(animate); } animate(); // Resize handler window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; CENTER.x = canvas.width / 2; CENTER.y = canvas.height / 2; }); </script> </body> </html> This file includes: 1. A spinning heptagon with 7 sides. 2. 20 numbered balls with unique colors, each with its own rotation. 3. Realistic physics for gravity, friction, and collisions (between balls and the heptagon walls). 4. Dynamic resizing for the canvas.