oneshotlm
Compare all models on this prompt →

Wolfenstein raycaster · kimi-k3

Output saved · Stopped by an API error Cost / run $0.1143 Runtime 121.2s

A model request failed and its retries ran out. The output is the HTML saved before that.

Prompt for this run

Build a playable first-person raycasting maze shooter on a canvas in a Wolfenstein-inspired style. Use HTML, CSS, and JavaScript as needed. Write it to `index.html` in the current working directory using your file tools. Inline your own CSS and JavaScript. Do not use external assets, CDNs, or network requests. No third-party libraries are provided. Required behavior: - Render a textured grid maze through a raycaster, with perspective wall heights and visible enemies. Generate textures procedurally; do not load external assets. - Use W/S to move forward/backward, A/D to strafe, Left/Right arrows to turn, and Space to shoot. Display the controls. Mouse turning is optional and must not be required. - Use solid wall collision and a small level with a reachable exit and at least three enemies. Enemies must be occluded by walls; shots cannot damage an enemy through a wall. - Give the player health. Enemies can damage the player under a clear attack rule. Show health and remaining enemy count; zero health causes Game Over. - Defeating all enemies unlocks the exit; reaching it shows a win state. Provide Restart to restore the level, player, enemies, and health. Verify: Test movement and turning near the starting area, check collision against a nearby wall, and fire to inspect weapon feedback. Sample the scene for stable wall rendering, then use Restart and check the initial viewpoint and health.

⛨ sandboxed Open in new tab ↗

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

modelkimi-k3
vendorMoonshot · Kimi
harnesspi 0.87.1 · browser iteration
generated2026-10-14

METRICS

runtime121.2s
tokens in7.3k
tokens out9.0k
cached2.7k
cost / run$0.1143
Transcript · 7 messages
system

user

Build a playable first-person raycasting maze shooter on a canvas in a Wolfenstein-inspired style. Use HTML, CSS, and JavaScript as needed. Write it to `index.html` in the current working directory using your file tools. Inline your own CSS and JavaScript. Do not use external assets, CDNs, or network requests. No third-party libraries are provided. Required behavior: - Render a textured grid maze through a raycaster, with perspective wall heights and visible enemies. Generate textures procedurally; do not load external assets. - Use W/S to move forward/backward, A/D to strafe, Left/Right arrows to turn, and Space to shoot. Display the controls. Mouse turning is optional and must not be required. - Use solid wall collision and a small level with a reachable exit and at least three enemies. Enemies must be occluded by walls; shots cannot damage an enemy through a wall. - Give the player health. Enemies can damage the player under a clear attack rule. Show health and remaining enemy count; zero health causes Game Over. - Defeating all enemies unlocks the exit; reaching it shows a win state. Provide Restart to restore the level, player, enemies, and health. Verify: Test movement and turning near the starting area, check collision against a nearby wall, and fire to inspect weapon feedback. Sample the scene for stable wall rendering, then use Restart and check the initial viewpoint and health.

assistant
thinking
Let me build a Wolfenstein-style raycaster in a single index.html. I need to write the whole thing, then test with browser tools.

Plan:
- Canvas, procedural textures (generate via offscreen canvas with noise patterns).
- Map grid, e.g., 16x16, with walls of different types and an exit cell.
- Player: x, y, dir, health. WASD + arrows + space.
- Enemies: sprites, simple AI (move toward player when line of sight, attack when close with cooldown), occlusion via zbuffer per column.
- Shooting: hitscan, check closest enemy within FOV center and no wall between.
- Exit locked until all enemies dead; draw exit as a special wall/cell.
- HUD: health, enemies remaining, message, restart button + R key.
- Weapon: simple gun drawn at bottom, muzzle flash on shoot, recoil.

Write index.html, then test with browser tool: screenshot, key presses, etc.

Let me write the code carefully.

Map: use strings. '1','2','3' wall types, 'E' exit, '.' empty, 'P' player start, numbers 'a','b','c' enemies positions.

Raycaster: standard DDA, per-column zbuffer. Textures: generate 64x64 patterns (brick, stone, exit door). Shade by distance and side.

Enemies: billboarded sprites drawn per column respecting zbuffer. Procedurally draw enemy sprite on offscreen canvas (simple humanoid with transparency).

Enemy AI: if distance < some range and line-of-sight (DDA ray between enemy and player), move toward player, if within attack range (0.8), attack with cooldown, reduce health, show red flash.

Shooting: ray from player along dir; find wall hit distance; for each alive enemy, compute angle difference and distance; if within small angular threshold and distance < wallDist along that ray (i.e., enemy closer than wall in center column or LOS check), kill it. Simplest: for each enemy, check angle to player dir < atan(halfWidth/dist), and lineOfSight via DDA stepping — then pick nearest. Damage 1 shot kill or 2 hits. Give enemies HP 1 for simplicity? "shots cannot damage enemy through wall" — LOS check handles it.

Exit: cell type 'E' acts as wall rendered with door texture until unlocked; when unlocked, walking into it wins. Or exit cell rendered as wall until unlocked then becomes passable — walking onto it shows win.

HUD: health bar, enemies count, controls text, messages.

Restart: button and R key resets state.

Also add minimap optional? Not required, skip to save time. Maybe small.

Mouse turning optional — skip or add pointer lock? "optional and must not be required" — I'll skip mouse to save time, or add simple. Skip.

Weapon feedback: gun bobs/recoils, muzzle flash, sound? No sound needed (can't load assets; could use WebAudio but optional). Keep visual.

Let me write the file.

Map design (16x16):

```
1111111111111111
1..............1
1..2..2.....3..1
1..............1
1..2.....2.....1
1.....P........1
1..............1
1....3....2....1
1..............1
1..2...........1
1......a.......1
1..............1
1.3.....2..b...1
1..............1
1..c..........E1
1111111111111111
```

Enemies a,b,c near bottom; exit at (14,14). Good.

Actually make corridors more maze-like. Let me design a better maze:

```
1111111111111111
1P....1........1
1.11..1..1111..1
1..1.....1..1..1
1..1..1111..1..1
1..1........1..1
1..11111..111..1
1.......a......1
1111..11111..111
1..1.......1...1
1..1..111..1.1.1
1..b...1...1.1.1
1..11111.111.1.1
1......1.....1.1
1..c...1.....1E1
1111111111111111
```

Check reachability... row1: "1P....1........1" — x0..15. Row2 "1.11..1..1111..1". Hmm, let me verify with a quick flood fill mentally or just write code and test reachability via a quick node script? I can run bash with node if available. Better: write the map, and run a small check script. Let me design and validate with node in bash.

Let me write index.html with the map as strings, then validate reachability of all enemies and exit from P via node.

Raycasting details:

```js
const TEX=64;
function castRay(angle){ DDA returns dist, side, texX, mapVal }
for(x=0;x<W;x+=RES) // RES=2 for perf maybe; use full width 480? canvas 640x400, step 2.
```

Use internal resolution: canvas width 640, columns step 2 → 320 rays. Fine.

Sprite rendering: for each enemy compute transform, screen x, height, then draw vertical strips checking zbuffer. Simpler: draw the sprite image with drawImage in slices of width stripW=2, checking zbuffer at that column. Standard approach.

Draw floor/ceiling: simple gradient fills (no floor casting) — acceptable and stable.

Textures generation:
- brick: rows of bricks with mortar lines, shading noise.
- stone: random blocks.
- door/exit: distinct, e.g., green door with "EXIT" pattern. When locked maybe draw a bar over it? When all enemies dead, exit opens: render exit cell texture differently or make passable.

Enemy sprite: 64x64 canvas, transparent bg, draw body (dark uniform), head, eyes that glow, gun. Fine.

Enemy behavior:
- states: idle until player within sight range & LOS → chase (speed 1.5 tiles/s), avoid walls (collision radius 0.2).
- if dist < 0.9: attack every 0.8s → player.health -= 10, red flash.
- Enemy HP: 2 shots? Keep 1 hit kill for quick testing but maybe 2. I'll do 2 hits with hit flash. Actually simpler: 1 shot kill. But to show "damage feedback", do HP=2, flash white when hit. Fine.

LOS function: step along line in small increments checking map solid. Solid includes exit cell when locked? For shooting through exit — exit cell counts as wall until unlocked.

Zbuffer: store per column distance. Sprites occluded correctly.

HUD: divs overlay or canvas text. Use DOM overlay for health/enemies/message and canvas for scene. Restart button in overlay.

Game states: playing, won, lost. Stop updates except restart.

Weapon: draw at bottom center: simple gun shape (dark polygon), recoil offset animates after shot, muzzle flash for a few frames. Crosshair center.

Controls listing displayed on screen.

Turn speed: ~2.5 rad/s with arrows. Move speed 3 tiles/s. Collision: check x and y separately with radius 0.2.

Frame-independent dt with performance.now.

Now write it. Also need to validate with browser tool: load page, screenshot, press keys (key action with text like "w"? The browser key action probably takes key names). I'll try screenshot, then key "w" or type. The key action — send e.g. {action:"key", text:"ArrowRight"}. I'll test.

Let me write the file now.

Details for raycaster:

```js
function cast(px,py,angle){
  const rdx=Math.cos(angle), rdy=Math.sin(angle);
  let mapX=Math.floor(px), mapY=Math.floor(py);
  const ddx=Math.abs(1/(rdx||1e-9)), ddy=Math.abs(1/(rdy||1e-9));
  let stepX,stepY,sdx,sdy;
  if(rdx<0){stepX=-1;sdx=(px-mapX)*ddx;}else{stepX=1;sdx=(mapX+1-px)*ddx;}
  if(rdy<0){stepY=-1;sdy=(py-mapY)*ddy;}else{stepY=1;sdy=(mapY+1-py)*ddy;}
  let side=0, hit=0, val='1';
  for(let i=0;i<64 && !hit;i++){
    if(sdx<sdy){sdx+=ddx;mapX+=stepX;side=0;}else{sdy+=ddy;mapY+=stepY;side=1;}
    val=cellAt(mapX,mapY);
    if(isSolidVal(val)) hit=1;
  }
  let dist = side===0 ? sdx-ddx : sdy-ddy;
  // wallX
  let wallX = side===0 ? py+dist*rdy : px+dist*rdx;
  wallX-=Math.floor(wallX);
  return {dist,side,wallX,val};
}
```

isSolidVal: val !== '.' and not enemy markers; exit 'E' solid unless exitOpen.

Correct fisheye: dist * cos(angle - dir).

Rendering column: lineHeight = H / corrDist; draw textured slice from texture canvas via drawImage(tex, texX,0,1,64, x, y0, colW, lineH). Shade: fill black with alpha based on dist and side.

Enemies render after walls, sorted far→near. For each: relative pos, angle diff normalized, if |diff| < fov/2 + margin: screenX = (0.5 + diff/fov)*W; size = H/dist... use corrected perpendicular distance: dist*cos(diff). Then draw strips.

Per-column sprite occlusion: for sx from startX to endX step strip: texX accordingly; if zbuffer[col] > perpDist then drawImage(slice).

Attack flash: red overlay alpha.

Muzzle flash: draw yellow circle/star at gun tip few frames.

OK. Enemy hit flash: tint white — draw sprite then overlay white with globalCompositeOperation 'source-atop'? Simpler: pre-render a white version of enemy sprite and swap when recently hit.

Exit rendering: when locked, exit cell solid with door texture; when unlocked, it becomes non-solid and when player enters cell → win. Also maybe render a glowing portal? When unlocked and passable, raycaster passes through it; draw nothing special. Add HUD message "EXIT OPEN". Fine. To make exit visible when open, I could keep it solid but stepping into adjacent... no — simplest: when unlocked, treat 'E' as empty; win when floor(px),floor(py) equals exit coords.

But then the exit just looks like empty room — acceptable; put a "portal" sprite there when open: draw a sprite at exit center (green glowing door) that when player within 0.5 → win. That renders visibly. Do that: exit as sprite billboard, not solid; win on proximity. Before unlocked, exit cell solid wall with "locked door" texture (draw with red bars). Message "Exit locked — eliminate all enemies".

Good. Now write code.

Map strings — validate reachability. Let me pick map:

```
1111111111111111
1P....1........1
1.11..1..1111..1
1..1.....1.....1
1..1..11111.11.1
1..1...........1
1..11111..1111.1
1.....a......1.1
1111..11111..1.1
1..1....1....1.1
1..1.1..1.11...1
1..b.1.....11111  <- wait width 16
```

I'll just carefully make a clean 16x16 and flood-fill check via node before embedding. Let me do: write map in a small node script first.

Actually faster: embed map in HTML, then run node reading the map from the HTML with regex and flood fill. Or simply craft map carefully by hand with a checker script now. I'll write a quick node script defining map and BFS.

Let me do that in bash.
→ bash {"command":"cat << 'EOF' > /tmp/mapcheck.js\nconst MAP = [\n\"1111111111111111\",\n\"1P....1........1\",\n\"1.11..1..1111..1\",\n\"1..1.....
toolResult

a reachable: true b reachable: true c reachable: true E reachable: true open cells: 135

assistant
thinking
Map validated. Now write index.html with full game.

Map validated. Now writing the full game:

→ write index.html (15326 bytes)
toolResult

Successfully wrote to index.html

assistant