Space Invaders · grok-build-0.1
Build a playable Space Invaders game on a canvas: a player cannon that moves and shoots, a descending grid of aliens that advance and fire back, destructible bunkers, score, and lives. Procedural graphics only, no external assets.
transcript · 12 messages
Build a playable Space Invaders game on a canvas: a player cannon that moves and shoots, a descending grid of aliens that advance and fire back, destructible bunkers, score, and lives. Procedural graphics only, no external assets.
thinking
The task is to build a playable Space Invaders game in a single self-contained index.html file. It must include CSS in a <style> tag, JS in a <script> tag, no external resources, fully sandboxed.
First, the user wants a playable Space Invaders game in a single index.html file with procedural graphics only, no external assets. It needs to be self-contained with CSS in <style> and JS in <script>.
I need to build this and end with index.html present in /tmp/ws.
I have a write tool to create the file.
I need to write the entire HTML file content.
Plan the code:
- HTML structure: <!DOCTYPE html>, <html>, <head> with <style>, <body> with <canvas id="gameCanvas">, <div> for score and lives, maybe button.
- CSS for 100% width/height canvas, dark bg, green text.
- JS: script tag with all code.
Game logic in JS:
Use requestAnimationFrame for loop.
Handle keyboard events for movement and shooting.
Define entities.
For aliens: let's say 5 rows, 11 columns, but to keep simple, maybe 4 rows x 8 cols for performance, but classic is 11 wide.
Canvas width 800, height 600.
Player at bottom y=550, x=375 center.
Aliens start y=50, x from 50 to 750 step 60 or so.
For bunkers: 3 bunk 4 bunkers at bottom.
Each bunker: width 100px, height 50px, made of 4x4 blocks say 5x3 blocks.
Define a function to draw bunker with damage map.
Damage: array of arrays bool.
On hit, set to false.
For simplicity, use a list of 2d points or rects for each block.
Let's code it mentally.
I need to output the full code in the end.
Confidence: this is complex, but I know how to do it, so high confidence \confidence{70}
I need to write the code using the tool.{"command":"ls -la /tmp/ws"}total 0 drwxr-sr-x. 2 node node 6 Aug 1 20:48 . drwxrwsrwx. 3 root node 16 Aug 1 20:48 ..
thinking
The task is to build a playable Space Invaders game on a canvas in a single self-contained index.html file. Everything inline: CSS in <style>, JS in <script>. No external resources, CDNs, or network r...First, I need to create an index.html file with the game. Since it's self-contained, all in one file.
I need to use the write tool to write the content to /tmp/ws/index.html.
But the user says "I need to end with index.html present there." So probably write it and confirm.
The current directory is /tmp/ws, so I should write index.html there.
Plan the code.
Basic structure:
<!DOCTYPE html>
<html>
<head>
<title>Space Invaders</title>
<style>
body { background: black; margin: 0; }
canvas { display: block; margin: auto; background #000; }
#ui { position: absolute; top: 0; left: 0; color: white; }
</style>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div id="ui">Score: 0 Lives: 3 </div>
<script>
// JS code here
</script>
</body>
</html>
Width 800 height 600 for classic.
Now, JS:
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const ui = document.getElementById('ui');
// Constants
const WIDTH = 800;
const HEIGHT = 600;
const PLAYER_WIDTH = 50;
const PLAYER_HEIGHT = 20;
const PLAYER_Y = HEIGHT - 50;
const ALIEN_ROWS = 5;
const ALIEN_COLS = 11;
const ALIEN_WIDTH = 40;
const ALIEN_HEIGHT = 30;
const ALIEN_SPACING_X = 50;
const ALIEN_SPACING_Y = 40;
const BULLET_WIDTH = 4;
const BULLET_HEIGHT = 10;
const BUNKER_ROWS = 4;
const BUNKER_COLS = 6;
const B UNKER_CELL_SIZE = 8;
const BUNKER_WIDTH = BUNKER_COLS * BUNKER_CELL_SIZE;
const BUNKER_HEIGHT = BUNKER_ROWS * BUNKER_CELL_SIZE;
const BUNKER_SPACING = 50;
const BUNKER_Y = HEIGHT - 100;
// 4 bunkers: positions x = 50, 150, 450, 550 or something.
// Game variables
let playerX = WIDTH / 2 - PLAYER_WIDTH / 2;
let score = 0;
let lives = 3;
let aliens = [];
let bullets = [];
let enemy Bullets = [];
let alienDx = 1;
let alienDy = 0;
let alienSpeed = 50; // pixels per move
let lastAlienMove = 0;
let alienInterval = 500;
let playerBullets = [];
let enemyBullets = [];
let keys = {};
let gameRunning = true;
let startScreen = true;
let bunkerGrids = [];
// Initialize bunkers
function initBunkers() {
for(let i=0; i<4; i++) {
let bx = 50 + i*200;
let grid = createBunkerGrid();
bunkerGr ids.push({x: bx, y: BUNKER_Y, grid: grid});
}
}
function createBunkerGrid() {
return [
[1,1,1,1,1,1],
[1,1,1,1,1,1],
[1,1 1,1,1,1],
[1,1,1,1,1,1]
]; // simple all filled, or patterned
// better pattern
return [
[0,0,1,1,1,1],
[0,1,1,1, 1,1],
[1,1,1,1,1,1],
[1,1,1,0,0,0]
];
}
// Init aliens
function initAliens() {
aliens = [];
for(let r=0; r<ALIEN_ROWS; r++) {
for(let c=0; c<ALIEN_COLS; c++) {
let x = ALIEN_START_X + c * ALIEN_SPACING_X;
let y = ALIEN_START_Y + r * ALIEN_SPACING_Y;
aliens.push({x, y, alive: true, row: 4-r, type: r});
}
}
}
const ALIEN_START_X = 50;
const ALIEN_START_Y = 50;
// Add event listeners
document.addEventListener('keydown', e => {
keys[e.key] = true;
if(e.key === ' ' && !e.repeat && startScreen) {
startScreen = false;
initGame();
}
if(e.key === ' ' && !startScreen && playerBullets.length <2) {
firePlayerBullet();
}
});
document.addEventListener('keyup', e => {
keys[e.key] = false;
});
// Game loop
function gameLoop() {
if(!gameRunning) return;
update();
draw();
requestAnimationFrame(gameLoop);
}
function update() {
if(startScreen) return;
// Player movement
if(keys['ArrowLeft'] || keys['a']) playerX -= 5 if(playerX >0);
if(keys['ArrowRight'] || keys['d']) playerX +=5 if(playerX <WIDTH -PLAYER_WIDTH);
// Shoot
// Already in keydown but to cooldown
// Better use cooldown
// Let's add cooldown var
// I need to add let shootCooldown =0;
// In update: if(shootCooldown >0) shootCooldown--;
// if(keys[' '] && shootCooldown <=0) {
// firePlayerBullet();
// shootCooldown = 10;
// }
// Yes, move fire to update.
// Move aliens if(Date.now() - lastAlienMove > alienInterval) {
moveAliens();
lastAlienMove = Date.now();
}
// Move bullets
moveBullets();
// Check collisions
checkCollisions();
// Check win
if(aliens.every(a=> !a.alive)) {
nextWave();
}
// Check lose
if(lives <=0) gameOver();
// Check alien reach player
checkAlienPlayerCollision();
}
function draw() {
ctx.fillStyle = '#000';
ctx.fillRect(0,0,WIDTH,HEIGHT);
// Draw stars? Optional
// Draw bunkers
bunkerGrids.forEach(b => drawBunker(b));
// Draw aliens
aliens.forEach(a => if(a.alive) drawAlien(a.x, a.y, a.row));
// Draw player
drawPlayer(playerX, PLAYER_Y);
// Draw bullets
bullets.forEach(b => drawBullet(b.x, b.y));
enemyBullets.forEach(b => drawBullet(b.x, b.y, 'red'));
// Draw UI
ui.innerHTML = `Score: ${score} Lives: ${lives}`;
if(startScreen) {
ctx.fillStyle = '#fff';
ctx.font = '30px Arial';
ctx.fillText('SPACE INVADERS', WIDTH/2 -200, HEIGHT/2 -50);
ctx.font = '20px Arial';
ctx.fillText('Use arrow keys to move, space to shoot', WIDTH/ 2 -300, HEIGHT/2);
ctx.fillText('Press space to start', WIDTH/2 -100, HEIGHT/2 +50);
}
if(!gameRunning) {
ctx.fillStyle = '#fff';
ctx.font = '30px Arial';
ctx.fillText('GAME OVER', WIDTH /2 -100, HEIGHT/2);
}
}
// Implement functions one by one.
This is getting long, I need to write the full code.
Since the task is to build it, I will use the tool to write a complete code.
I need to craft a complete index.html.
Let me try to write a minimal version first, then expand.
To save time, I recall there are standard codes for this.
But since no external, I have to write it.
Let's try to assemble a working code.
Let's write it.
First, the HTML part.
Then in script:
let canvas = document.getElementById(' gameCanvas');
let ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
let player = {x: 375, y: 550, width: 50, height: 20};
let score = 0;
let lives = 3;
let aliens = [];
let bullets = [];
let enemyBullets = [];
let alienDirection = 1;
let alienSpeed = 1;
let alienMoveInterval = 1000;
let lastAlienMoveTime = 0;
let keys = {};
let gameState = 'start';
let shootCooldown = 0;
let bunkerGr ids = [];
let level = 1;
// Init bunkers
function initBunkers() {
bunkerGrids = [];
for(let i =0; i<4; i++) {
let x = 50 + i * 150;
let grid = [
[1,1, 1,1,1,1],
[1,1,1,1,1,1],
[1,1,1,1,1,1],
[1,1,1,1,1,1]
]; // simple
bunkerGrids.push({x: x , y: 400, grid: grid});
}
}
// Init aliens
function initAliens() {
aliens = [];
let startX = 50;
let startY = 50;
for(let r =0; r<5; r++) {
for(let c=0; c<11; c++) {
aliens.push({
x: startX + c * 60,
y: startY + r * 40,
width: 40,
height: 30,
alive: true,
row: r
});
}
}
}
function fire PlayerBullet() {
bullets.push({x: player.x + player.width /2 -2, y: player.y, width:4, height:10, dy: -10});
}
function moveBullets() {
for(let i = bullets.length -1; i>=0; i--) {
let b = bullets[i];
b.y += b.dy;
if(b.y <0) bullets.splice(i,1);
}
for(let i = enemyBullets.length -1; i>=0; i--) {
let b = enemyBullets[i];
b./tmp/ws/index.html (16094 bytes)Successfully wrote 16094 bytes to /tmp/ws/index.html
thinking
The file is written. I need to verify it exists and test it somehow. Since it's a self-contained HTML, I can check the file and perhaps use a simple way to validate syntax.
{"command":"ls -la /tmp/ws/index.html && wc -c /tmp/ws/index.html"}-rw-r--r--. 1 node node 16108 Aug 1 20:50 /tmp/ws/index.html 16108 /tmp/ws/index.html
thinking
The file has been created successfully. It's a single self-contained index.html with the full Space Invaders game implemented using canvas, pure procedural graphics (no external assets), inline CSS an...
{"command":"head -n 30 /tmp/ws/index.html && echo \"...\" && tail -n 10 /tmp/ws/index.html"}<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SPACE INVADERS</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #000; color: #0f0; font-family: 'Courier New', Courier, monospace; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; overflow: hidden; } h1 { font-size: 28px; letter-spacing: 8px; margin-bottom: 8px; text-shadow: 0 0 10px #0f0; } canvas { border: 3px solid #0a0; background: #000; box-shadow: 0 0 20px rgba(0, 255, 0, 0.3); image-rendering: crisp-edges; ... // Kick off lastAlienMove = Date.now(); lastEnemyFire = Date.now(); gameLoop(); } window.onload = startGame; </script> </body> </html>
thinking
The file is created successfully. It's a complete, self-contained Space Invaders game in a single index.html file.
{"command":"echo \"File present:\" && ls -lh /tmp/ws/index.html && echo \"\" && echo \"No external refs check:\" && grep -oE 'src=|href=|httFile present: -rw-r--r--. 1 node node 16K Aug 1 20:50 /tmp/ws/index.html No external refs check:
thinking
The file is complete and self-contained. It meets all requirements:
The single-file self-contained Space Invaders game has been created at `/tmp/ws/index.html`. **Features implemented:** - 640×720 canvas with retro green-on-black aesthetic - Player cannon: keyboard move (←/→ or A/D) + Space to fire (limited to 2 bullets) - 5×11 alien grid with classic shapes and row-based point values (10/20/30) - Aliens move horizontally, drop on edge hits, increase speed as they die, and eventually fire back - Player and bunker-damaging enemy bullets - 4 destructible bunkers with pixel-cell destruction (no holes pre-cut) - Score, lives (3), and level/wave counter - Progressive difficulty: faster aliens, higher fire rate per wave - Procedural graphics only: all drawn with `fillRect` (aliens, player, bullets, bunkers, stars) - States: start screen, gameplay, wave complete, game over + restart (Space or R) - Fully offline — no external files, CDNs, images, or network requests Open `index.html` directly in any browser to play.
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
EVALUATION
Space Invaders starts on keyboard input with player ship, bunkers, aliens, and bullets; mouse input has no effect.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.