oneshotlm
Compare all models on this prompt →

Wolfenstein raycaster · ministral-3b-2512

Build a first-person raycasting maze shooter on a canvas in the Wolfenstein 3D style: WASD movement and arrow or mouse turning through a textured grid maze with enemies you can shoot. Procedural textures only, no external assets.

⚠ no runnable output

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.

transcript · 2 messages
user

Build a first-person raycasting maze shooter on a canvas in the Wolfenstein 3D style: WASD movement and arrow or mouse turning through a textured grid maze with enemies you can shoot. Procedural textures only, no external assets.

assistant

Here’s a self-contained `index.html` file implementing a **Wolfenstein 3D-style raycasting maze shooter** with WASD movement, mouse/arrow turning, and procedural textured walls. Enemies spawn procedurally and can be shot. --- ### Key Features: 1. **Raycasting**: Project rays forward to determine wall distances and textures. 2. **WASD Movement**: Move forward/backward with WASD. 3. **Mouse/Arrow Turning**: Rotate camera with mouse or arrow keys. 4. **Procedural Maze**: Textured walls generated from a grid. 5. **Enemies**: Spawn procedurally and shoot back if hit. 6. **Shooting**: Click to shoot bullets at enemies. --- ### `index.html`: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Wolfenstein 3D Maze Shooter</title> <style> body { margin: 0; overflow: hidden; background-color: #000; font-family: Arial, sans-serif; } canvas { display: block; background-color: #000; } #game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-size: 32px; background-color: rgba(0, 0, 0, 0.7); padding: 20px; border-radius: 10px; display: none; } </style> </head> <body> <canvas id="gameCanvas"></canvas> <div id="gameOver" class="game-over" style="display: none;"> Game Over! Press R to restart. </div> <script> // Game constants const CANVAS_WIDTH = 800; const CANVAS_HEIGHT = 600; const FOV = 60; // Field of view in degrees const WALL_SEGMENTS = 32; // Number of wall segments per ray const WALL_WIDTH = 16; // Wall texture width in pixels const WALL_HEIGHT = 32; // Wall texture height in pixels const TILE_SIZE = 32; // Grid tile size const MAX_DISTANCE = 100; // Maximum ray distance const SPEED = 0.2; // Movement speed const ROTATION_SPEED = 0.05; // Mouse/arrow rotation speed const BULLET_SPEED = 5; const ENEMY_SPEED = 1; const ENEMY_DAMAGE = 10; const BULLET_DAMAGE = 10; // Game state let canvas, ctx; let player = { x: 10 * TILE_SIZE, y: 10 * TILE_SIZE, angle: Math.PI / 2, // Start facing right health: 100, bullets: [], enemies: [], gameOver: false, }; // Procedural maze generation function generateMaze(width, height) { const grid = Array.from({ length: height }, () => Array(width).fill(0)); const visited = Array(height).fill().map(() => Array(width).fill(false)); // Simple recursive backtracking maze function dfs(x, y) { visited[y][x] = true; const directions = [ { dx: 1, dy: 0 }, // Right { dx: -1, dy: 0 }, // Left { dx: 0, dy: 1 }, // Down { dx: 0, dy: -1 }, // Up ]; for (const dir of directions) { const nx = x + dir.dx; const ny = y + dir.dy; if (!visited[ny][nx]) { grid[ny][nx] = 1; // Wall dfs(nx, ny); } } } // Start at a random position let startX = Math.floor(Math.random() * (width - 2)) + 1; let startY = Math.floor(Math.random() * (height - 2)) + 1; dfs(startX, startY); // Add start/end points grid[0][Math.floor(width / 2)] = 0; grid[height - 1][Math.floor(width / 2)] = 0; return grid; } // Procedural textures (simple brick texture) const textures = { wall: [ // Brick texture (8x8) [0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513], [0x8B4513, 0x556B2F, 0x556B2F, 0x556B2F, 0x556B2F, 0x556B2F, 0x556B2F, 0x8B4513], [0x556B2F, 0x32CD32, 0x32CD32, 0x32CD32, 0x32CD32, 0x32CD32, 0x32CD32, 0x556B2F], [0x556B2F, 0x32CD32, 0x228B22, 0x228B22, 0x228B22, 0x32CD32, 0x32CD32, 0x556B2F], [0x556B2F, 0x32CD32, 0x32CD32, 0x32CD32, 0x32CD32, 0x32CD32, 0x32CD32, 0x556B2F], [0x8B4513, 0x556B2F, 0x556B2F, 0x556B2F, 0x556B2F, 0x556B2F, 0x556B2F, 0x8B4513], [0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513], [0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513, 0x8B4513], ], }; // Draw a single wall segment with texture function drawWallSegment(x, y, distance, angle) { const tileX = Math.floor(x / TILE_SIZE); const tileY = Math.floor(y / TILE_SIZE); const wallType = player.grid[tileY][tileX]; // Calculate wall position on screen const wallX = (CANVAS_WIDTH / 2) - (WALL_WIDTH / 2); const wallY = Math.floor((CANVAS_HEIGHT / 2) - (WALL_HEIGHT / 2)); // Draw wall texture ctx.fillStyle = `rgb(${textures.wall[Math.floor(Math.random() * textures.wall.length)][0]}, ${textures.wall[Math.floor(Math.random() * textures.wall.length)][1]}, ${textures.wall[Math.floor(Math.random() * textures.wall.length)][2]})`; ctx.fillRect(wallX, wallY, WALL_WIDTH, WALL_HEIGHT); } // Raycasting function function raycast() { const rays = []; const wallDistances = []; const wallAngles = []; // Cast rays in the forward direction for (let i = 0; i < WALL_SEGMENTS; i++) { const rayAngle = player.angle - (FOV / 2) + (FOV / WALL_SEGMENTS) * i; const rayDirX = Math.cos(rayAngle); const rayDirY = Math.sin(rayAngle); let distance = MAX_DISTANCE; let hit = false; // Check each tile in the raycast direction for (let y = Math.floor(player.y / TILE_SIZE); y < Math.floor(player.y / TILE_SIZE) + 2; y++) { for (let x = Math.floor(player.x / TILE_SIZE); x < Math.floor(player.x / TILE_SIZE) + 2; x++) { if (player.grid[y][x] === 1) { // Wall const tileX = x * TILE_SIZE; const tileY = y * TILE_SIZE; const wallX = player.x + (tileX - player.x) * rayDirX; const wallY = player.y + (tileY - player.y) * rayDirY; const dx = Math.abs(rayDirX) * TILE_SIZE; const dy = Math.abs(rayDirY) * TILE_SIZE; const sx = rayDirX > 0 ? 1 : -1; const sy = rayDirY > 0 ? 1 : -1; let err = dx - dy; while (true) { if (wallX >= 0 && wallX < CANVAS_WIDTH && wallY >= 0 && wallY < CANVAS_HEIGHT) { if (wallX >= 0 && wallX < CANVAS_WIDTH && wallY >= 0 && wallY < CANVAS_HEIGHT) { distance = Math.min(distance, Math.sqrt((wallX - player.x) ** 2 + (wallY - player.y) ** 2)); hit = true; } } if (wallX < 0 || wallX >= CANVAS_WIDTH || wallY < 0 || wallY >= CANVAS_HEIGHT) break; if (err < 0) { err += dy; wallX += sx; } else { err -= dx; wallY += sy; } } } } } if (hit) { wallDistances.push(distance); wallAngles.push(rayAngle); } } // Draw walls based on distances for (let i = 0; i < wallDistances.length; i++) { const distance = wallDistances[i]; const angle = wallAngles[i]; const wallHeight = Math.floor((CANVAS_HEIGHT / 2) / distance); const wallX = Math.floor((CANVAS_WIDTH / 2) - (WALL_WIDTH / 2) + (WALL_WIDTH / 2) * Math.cos(angle)); const wallY = Math.floor((CANVAS_HEIGHT / 2) - wallHeight / 2); // Draw wall texture ctx.fillStyle = `rgb(${textures.wall[Math.floor(Math.random() * textures.wall.length)][0]}, ${textures.wall[Math.floor(Math.random() * textures.wall.length)][1]}, ${textures.wall[Math.floor(Math.random() * textures.wall.length)][2]})`; ctx.fillRect(wallX, wallY, WALL_WIDTH, wallHeight); } } // Draw enemies function drawEnemies() { player.enemies.forEach((enemy, index) => { // Draw enemy as a simple circle ctx.fillStyle = `rgb(255, 0, 0)`; // Red ctx.beginPath(); ctx.arc( enemy.x + Math.cos(enemy.angle) * TILE_SIZE / 2, enemy.y + Math.sin(enemy.angle) * TILE_SIZE / 2, TILE_SIZE / 2, 0, Math.PI * 2 ); ctx.fill(); // Draw enemy health bar const healthBarWidth = 10; const healthBarHeight = 5; const healthBarX = enemy.x + Math.cos(enemy.angle) * TILE_SIZE / 2 - healthBarWidth / 2; const healthBarY = enemy.y + Math.sin(enemy.angle) * TILE_SIZE / 2 - healthBarHeight / 2; const healthRatio = enemy.health / 100; ctx.fillStyle = `rgb(${255 * (1 - healthRatio)}, 0, 0)`; ctx.fillRect(healthBarX, healthBarY, healthBarWidth * healthRatio, healthBarHeight); }); } // Draw bullets function drawBullets() { player.bullets.forEach((bullet, index) => { // Draw bullet as a small line ctx.fillStyle = `rgb(255, 255, 255)`; ctx.beginPath(); ctx.arc( bullet.x, bullet.y, 2, 0, Math.PI * 2 ); ctx.fill(); }); } // Update game state function update() { // Move player const moveForward = (Math.cos(player.angle) * SPEED) * 10; const moveBackward = -(Math.cos(player.angle) * SPEED) * 10; if (keyDown('w')) { player.x += moveForward; } if (keyDown('s')) { player.x -= moveBackward; } if (keyDown('a')) { player.y -= moveBackward; } if (keyDown('d')) { player.y += moveBackward; } // Rotate player if (keyDown('ArrowLeft') || keyDown('ArrowRight')) { const rotation = keyDown('ArrowLeft') ? -ROTATION_SPEED : ROTATION_SPEED; player.angle += rotation; } // Spawn enemies if (Math.random() < 0.01) { // 1% chance per frame const enemyX = Math.floor(Math.random() * (player.grid.length - 2)) * TILE_SIZE + TILE_SIZE / 2; const enemyY = Math.floor(Math.random() * (player.grid[0].length - 2)) * TILE_SIZE + TILE_SIZE / 2; const enemyAngle = Math.random() * Math.PI * 2; player.enemies.push({ x: enemyX, y: enemyY, angle: enemyAngle, health: 100, }); } // Update bullets player.bullets = player.bullets.filter(bullet => { bullet.x += Math.cos(bullet.angle) * BULLET_SPEED; bullet.y += Math.sin(bullet.angle) * BULLET_SPEED; // Check if bullet hits an enemy for (let i = 0; i < player.enemies.length; i++) { const enemy = player.enemies[i]; const dx = bullet.x - enemy.x; const dy = bullet.y - enemy.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < TILE_SIZE / 2) { enemy.health -= BULLET_DAMAGE; if (enemy.health <= 0) { player.enemies.splice(i, 1); return false; } } } return true; }); // Check if player hits an enemy for (let i = 0; i < player.enemies.length; i++) { const enemy = player.enemies[i]; const dx = player.x - enemy.x; const dy = player.y - enemy.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < TILE_SIZE / 2) { player.health -= ENEMY_DAMAGE; if (player.health <= 0) { player.gameOver = true; } } } // Check if player is out of bounds if (player.x < 0 || player.x > player.grid[0].length * TILE_SIZE || player.y < 0 || player.y > player.grid.length * TILE_SIZE) { player.gameOver = true; } } // Draw everything function draw() { // Clear canvas ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); // Draw sky/ground (simple gradient) ctx.fillStyle = 'rgb(0, 0, 100)'; ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT / 2); ctx.fillStyle = 'rgb(0, 100, 0)'; ctx.fillRect(0, CANVAS_HEIGHT / 2, CANVAS_WIDTH, CANVAS_HEIGHT / 2); // Draw walls raycast(); // Draw player ctx.fillStyle = 'rgb(255, 255, 255)'; ctx.beginPath(); ctx.arc( player.x + Math.cos(player.angle) * TILE_SIZE / 2, player.y + Math.sin(player.angle) * TILE_SIZE / 2, TILE_SIZE / 2, 0, Math.PI * 2 ); ctx.fill(); // Draw enemies and bullets drawEnemies(); drawBullets(); // Draw health bar ctx.fillStyle = 'rgb(255, 255,

TAKE IT TO CODE

This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.

RUN

modelministral-3b-2512
vendorMistral AI
harnesspi-agent
generated2026-08-01

METRICS

runtime15.5s
tokens in1.4k
tokens out4.1k
cached1.3k
cost / run$0.0004