View Raw Turn JSON
{
"assistant": {
"text": "Added resetBall() to return ball to center with random horizontal direction. Implemented loseLife() function that decrements lives, checks for game over (game.lost=true, game.running=false), or resets ball if lives remain. Ball now stops moving when game.running=false. Added spacebar to start game. Added UI overlays: \"Press SPACE to Start\" before game begins, \"GAME OVER\" with final score when lives reach 0. Lives system fully functional.",
"type": "message"
},
"context": {
"artifacts": [
{
"content": "// Arkanoid Game - PrincipleSkinner Method (Ralph + GAIT)\n// Turn 1: Canvas foundation and game initialization\n// Turn 2: Paddle object with rendering\n// Turn 3: Paddle keyboard controls\n// Turn 4: Ball object with physics\n// Turn 5: Ball-paddle collision detection\n// Turn 6: Brick grid system\n// Turn 7: Ball-brick collision detection\n// Turn 8: Lives system and ball reset\n\n// Canvas setup\nconst canvas = document.getElementById('gameCanvas');\nconst ctx = canvas.getContext('2d');\n\n// Game state\nconst game = {\n score: 0,\n lives: 3,\n running: false,\n won: false,\n lost: false\n};\n\n// Paddle object\nconst paddle = {\n width: 100,\n height: 15,\n x: canvas.width / 2 - 50,\n y: canvas.height - 40,\n speed: 7,\n dx: 0\n};\n\n// Ball object\nconst ball = {\n radius: 8,\n x: canvas.width / 2,\n y: canvas.height / 2,\n dx: 4,\n dy: -4,\n speed: 4\n};\n\n// Brick configuration\nconst brickConfig = {\n rows: 5,\n cols: 10,\n width: 70,\n height: 20,\n padding: 10,\n offsetTop: 60,\n offsetLeft: 35\n};\n\n// Brick colors by row\nconst brickColors = ['#ff0000', '#ff7700', '#ffff00', '#00ff00', '#0077ff'];\n\n// Create brick grid\nconst bricks = [];\nfunction createBricks() {\n for (let row = 0; row < brickConfig.rows; row++) {\n bricks[row] = [];\n for (let col = 0; col < brickConfig.cols; col++) {\n bricks[row][col] = {\n x: col * (brickConfig.width + brickConfig.padding) + brickConfig.offsetLeft,\n y: row * (brickConfig.height + brickConfig.padding) + brickConfig.offsetTop,\n status: 1, // 1 = visible, 0 = destroyed\n color: brickColors[row]\n };\n }\n }\n}\n\n// Draw paddle\nfunction drawPaddle() {\n ctx.fillStyle = '#00ff00';\n ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);\n\n // Add glow effect\n ctx.shadowBlur = 10;\n ctx.shadowColor = '#00ff00';\n ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);\n ctx.shadowBlur = 0;\n}\n\n// Draw ball\nfunction drawBall() {\n ctx.beginPath();\n ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);\n ctx.fillStyle = '#00ffff';\n ctx.fill();\n\n // Add glow effect\n ctx.shadowBlur = 15;\n ctx.shadowColor = '#00ffff';\n ctx.fill();\n ctx.shadowBlur = 0;\n ctx.closePath();\n}\n\n// Draw bricks\nfunction drawBricks() {\n for (let row = 0; row < brickConfig.rows; row++) {\n for (let col = 0; col < brickConfig.cols; col++) {\n const brick = bricks[row][col];\n if (brick.status === 1) {\n ctx.fillStyle = brick.color;\n ctx.fillRect(brick.x, brick.y, brickConfig.width, brickConfig.height);\n\n // Add border\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n ctx.strokeRect(brick.x, brick.y, brickConfig.width, brickConfig.height);\n }\n }\n }\n}\n\n// Update UI\nfunction updateUI() {\n document.getElementById('score').textContent = game.score;\n document.getElementById('lives').textContent = game.lives;\n}\n\n// Update paddle position\nfunction updatePaddle() {\n paddle.x += paddle.dx;\n\n // Keep paddle within bounds\n if (paddle.x < 0) {\n paddle.x = 0;\n }\n if (paddle.x + paddle.width > canvas.width) {\n paddle.x = canvas.width - paddle.width;\n }\n}\n\n// Check ball-paddle collision\nfunction checkPaddleCollision() {\n // Ball is at paddle height\n if (ball.y + ball.radius >= paddle.y &&\n ball.y - ball.radius <= paddle.y + paddle.height) {\n // Ball is within paddle width\n if (ball.x >= paddle.x && ball.x <= paddle.x + paddle.width) {\n // Reverse ball direction\n ball.dy = -Math.abs(ball.dy);\n\n // Add spin based on where ball hits paddle\n const hitPos = (ball.x - paddle.x) / paddle.width; // 0 to 1\n ball.dx = (hitPos - 0.5) * 8; // -4 to 4 range for spin effect\n }\n }\n}\n\n// Check ball-brick collisions\nfunction checkBrickCollision() {\n for (let row = 0; row < brickConfig.rows; row++) {\n for (let col = 0; col < brickConfig.cols; col++) {\n const brick = bricks[row][col];\n if (brick.status === 1) {\n // Check if ball overlaps with brick\n if (ball.x + ball.radius > brick.x &&\n ball.x - ball.radius < brick.x + brickConfig.width &&\n ball.y + ball.radius > brick.y &&\n ball.y - ball.radius < brick.y + brickConfig.height) {\n\n // Destroy brick\n brick.status = 0;\n\n // Bounce ball\n ball.dy = -ball.dy;\n\n // Increase score\n game.score += 10;\n updateUI();\n\n return; // Only one brick per frame\n }\n }\n }\n }\n}\n\n// Reset ball to center\nfunction resetBall() {\n ball.x = canvas.width / 2;\n ball.y = canvas.height / 2;\n ball.dx = 4 * (Math.random() > 0.5 ? 1 : -1); // Random direction\n ball.dy = -4;\n}\n\n// Lose a life\nfunction loseLife() {\n game.lives--;\n updateUI();\n\n if (game.lives <= 0) {\n game.lost = true;\n game.running = false;\n } else {\n resetBall();\n }\n}\n\n// Update ball position and handle wall collisions\nfunction updateBall() {\n if (!game.running) return;\n\n ball.x += ball.dx;\n ball.y += ball.dy;\n\n // Wall collisions (left and right)\n if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {\n ball.dx = -ball.dx;\n }\n\n // Ceiling collision\n if (ball.y - ball.radius < 0) {\n ball.dy = -ball.dy;\n }\n\n // Check collisions\n checkPaddleCollision();\n checkBrickCollision();\n\n // Ball falls below paddle - lose life\n if (ball.y + ball.radius > canvas.height) {\n loseLife();\n }\n}\n\n// Clear canvas\nfunction clearCanvas() {\n ctx.fillStyle = '#000000';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n}\n\n// Render game\nfunction render() {\n clearCanvas();\n drawBricks();\n drawPaddle();\n drawBall();\n\n // Show start message\n if (!game.running && !game.lost) {\n ctx.fillStyle = '#ffffff';\n ctx.font = '24px Arial';\n ctx.textAlign = 'center';\n ctx.fillText('Press SPACE to Start', canvas.width / 2, canvas.height / 2 + 100);\n }\n\n // Show game over message\n if (game.lost) {\n ctx.fillStyle = '#ff0000';\n ctx.font = '48px Arial';\n ctx.textAlign = 'center';\n ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2);\n ctx.font = '20px Arial';\n ctx.fillStyle = '#ffffff';\n ctx.fillText('Final Score: ' + game.score, canvas.width / 2, canvas.height / 2 + 40);\n }\n}\n\n// Game loop\nfunction gameLoop() {\n updatePaddle();\n updateBall();\n render();\n requestAnimationFrame(gameLoop);\n}\n\n// Keyboard controls\nfunction keyDown(e) {\n if (e.key === 'ArrowLeft' || e.key === 'a') {\n paddle.dx = -paddle.speed;\n } else if (e.key === 'ArrowRight' || e.key === 'd') {\n paddle.dx = paddle.speed;\n } else if (e.key === ' ' && !game.running && !game.lost) {\n // Start game with spacebar\n game.running = true;\n }\n}\n\nfunction keyUp(e) {\n if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'ArrowRight' || e.key === 'd') {\n paddle.dx = 0;\n }\n}\n\n// Initialize game\nfunction init() {\n console.log('Arkanoid initialized - Canvas: ' + canvas.width + 'x' + canvas.height);\n createBricks();\n updateUI();\n render();\n\n // Add event listeners\n document.addEventListener('keydown', keyDown);\n document.addEventListener('keyup', keyUp);\n\n // Start game loop\n gameLoop();\n}\n\n// Start game on load\nwindow.addEventListener('load', init);",
"path": "game.js"
}
],
"pinned_context": {
"branch": "principalskinner-arkanoid",
"items": [],
"memory_id": "086e39ec4fb04bd965bcc97353b8c325dcc80d406e6b5b840ae8db97796c6775",
"pinned_items": 0,
"schema": "gait.context.v0"
}
},
"created_at": "2026-01-09T09:33:31",
"model": {
"provider": "vscode-copilot"
},
"schema": "gait.turn.v0",
"tokens": {
"by_role": {},
"estimated": true,
"input_total": 10,
"output_total": 93
},
"tools": {},
"user": {
"text": "Turn 8: Implement lives system and ball reset",
"type": "message"
},
"visibility": "private"
}