| <!DOCTYPE html> |
| <html> |
| <head> |
| <style> |
| body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #1a1a1a; } |
| .controls { position: fixed; top: 10px; left: 10px; color: white; } |
| button { margin: 5px; padding: 5px 10px; } |
| #gameArea { cursor: crosshair; } |
| .info-panel { |
| position: fixed; |
| top: 10px; |
| right: 10px; |
| color: white; |
| background: rgba(0,0,0,0.7); |
| padding: 15px; |
| border-radius: 5px; |
| font-family: Arial, sans-serif; |
| } |
| .score { |
| font-size: 24px; |
| font-weight: bold; |
| margin-bottom: 10px; |
| } |
| .points-popup { |
| position: absolute; |
| color: #4CAF50; |
| font-weight: bold; |
| font-family: Arial, sans-serif; |
| pointer-events: none; |
| animation: floatUp 1s ease-out forwards; |
| } |
| @keyframes floatUp { |
| 0% { transform: translateY(0); opacity: 1; } |
| 100% { transform: translateY(-50px); opacity: 0; } |
| } |
| .drawing-guide { |
| position: fixed; |
| bottom: 10px; |
| left: 10px; |
| color: white; |
| background: rgba(0,0,0,0.7); |
| padding: 10px; |
| border-radius: 5px; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="controls"> |
| <button onclick="addRobot()">Add Random Robot (+5 pts)</button> |
| <button onclick="togglePause()">Pause/Play</button> |
| </div> |
| <div class="info-panel"> |
| <div class="score">Score: <span id="scoreDisplay">0</span></div> |
| <div>Robots: <span id="robotCount">0</span></div> |
| </div> |
| <div class="drawing-guide"> |
| Click and drag to draw new robots (+5 pts). Collisions give +5 pts and growth! |
| </div> |
| <svg id="gameArea" width="800" height="600" style="background: #000;"> |
| <rect width="800" height="600" fill="none" stroke="#333" stroke-width="4"/> |
| <line id="drawingLine" stroke="white" stroke-width="2" stroke-dasharray="5,5" visibility="hidden"/> |
| </svg> |
|
|
| <script> |
| const svg = document.getElementById('gameArea'); |
| const drawingLine = document.getElementById('drawingLine'); |
| const robots = []; |
| let paused = false; |
| let isDrawing = false; |
| let startX, startY; |
| let score = 0; |
| |
| function updateScore(points, x, y) { |
| score += points; |
| document.getElementById('scoreDisplay').textContent = score; |
| document.getElementById('robotCount').textContent = robots.length; |
| |
| |
| const popup = document.createElement('div'); |
| popup.className = 'points-popup'; |
| popup.textContent = `+${points}`; |
| popup.style.left = `${x}px`; |
| popup.style.top = `${y}px`; |
| document.body.appendChild(popup); |
| |
| |
| setTimeout(() => popup.remove(), 1000); |
| } |
| |
| class Robot { |
| constructor(x, y, speed, direction, length) { |
| this.segments = []; |
| this.speed = speed || 2 + Math.random() * 2; |
| this.direction = direction || Math.random() * Math.PI * 2; |
| this.length = length || 5 + Math.floor(Math.random() * 5); |
| this.color = `hsl(${Math.random() * 360}, 80%, 50%)`; |
| this.x = x || Math.random() * 700 + 50; |
| this.y = y || Math.random() * 500 + 50; |
| |
| this.createSegments(); |
| } |
| |
| createSegments() { |
| |
| this.segments.forEach(seg => seg.element.remove()); |
| this.segments = []; |
| |
| |
| for (let i = 0; i < this.length; i++) { |
| const segment = document.createElementNS("http://www.w3.org/2000/svg", "circle"); |
| segment.setAttribute("r", "8"); |
| segment.setAttribute("fill", this.color); |
| svg.appendChild(segment); |
| this.segments.push({ |
| element: segment, |
| x: this.x, |
| y: this.y |
| }); |
| } |
| } |
| |
| grow() { |
| this.length += 1; |
| this.createSegments(); |
| } |
| |
| update() { |
| |
| this.x += Math.cos(this.direction) * this.speed; |
| this.y += Math.sin(this.direction) * this.speed; |
| |
| |
| if (this.x < 10 || this.x > 790) { |
| this.direction = Math.PI - this.direction; |
| this.mutate(); |
| } |
| if (this.y < 10 || this.y > 590) { |
| this.direction = -this.direction; |
| this.mutate(); |
| } |
| |
| |
| for (let i = this.segments.length - 1; i > 0; i--) { |
| this.segments[i].x = this.segments[i - 1].x; |
| this.segments[i].y = this.segments[i - 1].y; |
| } |
| this.segments[0].x = this.x; |
| this.segments[0].y = this.y; |
| |
| |
| this.segments.forEach(segment => { |
| segment.element.setAttribute("cx", segment.x); |
| segment.element.setAttribute("cy", segment.y); |
| }); |
| } |
| |
| mutate() { |
| |
| this.speed = Math.max(1, Math.min(6, this.speed + (Math.random() - 0.5))); |
| this.color = `hsl(${Math.random() * 360}, 80%, 50%)`; |
| this.segments.forEach(segment => { |
| segment.element.setAttribute("fill", this.color); |
| }); |
| } |
| |
| checkCollision(other) { |
| const dx = this.x - other.x; |
| const dy = this.y - other.y; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| |
| if (distance < 16) { |
| |
| const angle = Math.atan2(dy, dx); |
| this.direction = angle + Math.PI / 2; |
| other.direction = angle - Math.PI / 2; |
| |
| |
| this.grow(); |
| other.grow(); |
| this.mutate(); |
| other.mutate(); |
| |
| |
| const svgRect = svg.getBoundingClientRect(); |
| const popupX = svgRect.left + this.x; |
| const popupY = svgRect.top + this.y; |
| updateScore(5, popupX, popupY); |
| } |
| } |
| } |
| |
| |
| svg.addEventListener('mousedown', (e) => { |
| const rect = svg.getBoundingClientRect(); |
| startX = e.clientX - rect.left; |
| startY = e.clientY - rect.top; |
| isDrawing = true; |
| |
| drawingLine.setAttribute('x1', startX); |
| drawingLine.setAttribute('y1', startY); |
| drawingLine.setAttribute('x2', startX); |
| drawingLine.setAttribute('y2', startY); |
| drawingLine.setAttribute('visibility', 'visible'); |
| }); |
| |
| svg.addEventListener('mousemove', (e) => { |
| if (isDrawing) { |
| const rect = svg.getBoundingClientRect(); |
| const currentX = e.clientX - rect.left; |
| const currentY = e.clientY - rect.top; |
| |
| drawingLine.setAttribute('x2', currentX); |
| drawingLine.setAttribute('y2', currentY); |
| } |
| }); |
| |
| svg.addEventListener('mouseup', (e) => { |
| if (isDrawing) { |
| const rect = svg.getBoundingClientRect(); |
| const endX = e.clientX - rect.left; |
| const endY = e.clientY - rect.top; |
| |
| const dx = endX - startX; |
| const dy = endY - startY; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| const direction = Math.atan2(dy, dx); |
| |
| const speed = Math.min(6, distance / 50); |
| const length = Math.max(3, Math.min(12, Math.floor(distance / 30))); |
| robots.push(new Robot(startX, startY, speed, direction, length)); |
| |
| |
| updateScore(5, e.clientX, e.clientY); |
| |
| drawingLine.setAttribute('visibility', 'hidden'); |
| isDrawing = false; |
| } |
| }); |
| |
| svg.addEventListener('mouseleave', () => { |
| if (isDrawing) { |
| drawingLine.setAttribute('visibility', 'hidden'); |
| isDrawing = false; |
| } |
| }); |
| |
| function addRobot() { |
| const robot = new Robot(); |
| robots.push(robot); |
| |
| const svgRect = svg.getBoundingClientRect(); |
| updateScore(5, svgRect.left + robot.x, svgRect.top + robot.y); |
| } |
| |
| function togglePause() { |
| paused = !paused; |
| } |
| |
| function update() { |
| if (!paused) { |
| robots.forEach(robot => robot.update()); |
| |
| for (let i = 0; i < robots.length; i++) { |
| for (let j = i + 1; j < robots.length; j++) { |
| robots[i].checkCollision(robots[j]); |
| } |
| } |
| } |
| requestAnimationFrame(update); |
| } |
| |
| |
| for (let i = 0; i < 3; i++) { |
| addRobot(); |
| } |
| |
| update(); |
| </script> |
| </body> |
| </html> |