{"claude_md":"# Canvas Tilemap Renderer\n\nProcedural terrain generation with tile rendering, camera panning, and minimap overlay. Advanced HTML5 Canvas tutorial.\n\n## Quick Reference\n\n# Canvas Tilemap Renderer\n\n> **Difficulty:** Advanced | **Runtime:** HTML5 Canvas | **Output:** Interactive tilemap\n\nTeach an AI agent to generate procedural terrain, render it as a colored tile grid, implement camera panning, and display a minimap overlay — all in a single HTML file.\n\n## What You'll Build\n\nA self-contained HTML file featuring:\n- Procedural terrain generation using seeded random noise\n- Tile rendering with distinct terrain types (grass, water, sand, trees, mountains)\n- Arrow-key camera panning across a world larger than the viewport\n- A minimap overlay showing the full map with a camera indicator\n- Configurable world size and tile palette\n\n## Core Concepts\n\n### 1. Terrain Generation\n\nUse a simple value-noise approach:\n1. Create a 2D array of random values (seeded)\n2. Smooth with neighbor averaging\n3. Map value ranges to terrain types\n\n| Value Range | Terrain | Color |\n|------------|---------|-------|\n| 0.00–0.30 | Water | `#2563eb` |\n| 0.30–0.45 | Sand | `#eab308` |\n| 0.45–0.65 | Grass | `#16a34a` |\n| 0.65–0.80 | Trees | `#15803d` |\n| 0.80–1.00 | Mountain | `#78716c` |\n\n### 2. Camera System\n\nThe viewport shows a portion of the world:\n```\ncamera = { x: 0, y: 0 }\nvisible_cols = floor(canvas.width / TILE_SIZE)\nvisible_rows = floor(canvas.height / TILE_SIZE)\n```\n\nArrow keys shift the camera. Clamp to world bounds.\n\n### 3. Minimap\n\nA small (150x150) overlay in the corner:\n- Draws each tile as a 1-2px dot\n- Highlights the camera's current viewport as a white rectangle\n- Updates every frame\n\n### 4. Rendering Pipeline\n\nEach frame:\n1. Calculate visible tile range from camera position\n2. Draw only visible tiles (culling)\n3. Draw grid lines (optional)\n4. Draw minimap overlay\n5. Draw HUD (coordinates, terrain info)\n\n## Instructions\n\n1. Create the canvas and set up keyboard input\n2. Generate the world map using seeded noise\n3. Implement tile rendering with camera offset\n4. Add arrow-key panning with bounds checking\n5. Draw the minimap in the top-right corner\n6. Display camera coordinates as HUD\n\n## Parameters\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| `WORLD_W` | 100 | World width in tiles |\n| `WORLD_H` | 80 | World height in tiles |\n| `TILE_SIZE` | 12 | Tile size in pixels |\n| `SEED` | 42 | Random seed for terrain |\n| `SMOOTH_PASSES` | 3 | Noise smoothing iterations |\n\n## Agent Docs (read when relevant)\n\n| File | When to read |\n|------|-------------|\n| `agent_docs/tilemap.html` | tilemap |\n| `agent_docs/README.md` | README |\n","agent_docs":{"agent_docs/tilemap.html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<title>Canvas Tilemap Renderer</title>\n<style>\n  * { margin: 0; padding: 0; }\n  body { background: #0a0a0a; overflow: hidden; }\n  canvas { display: block; }\n</style>\n</head>\n<body>\n<canvas id=\"c\"></canvas>\n<script>\nconst canvas = document.getElementById('c');\nconst ctx = canvas.getContext('2d');\ncanvas.width = window.innerWidth;\ncanvas.height = window.innerHeight;\n\nconst WORLD_W = 100, WORLD_H = 80;\nconst TILE_SIZE = 12;\nconst SMOOTH_PASSES = 3;\nconst MINIMAP_SIZE = 150;\nconst MINIMAP_PAD = 10;\n\n// Seeded PRNG (simple LCG)\nlet seed = 42;\nfunction seededRandom() {\n  seed = (seed * 1664525 + 1013904223) & 0xFFFFFFFF;\n  return (seed >>> 0) / 0xFFFFFFFF;\n}\n\nconst TERRAIN = [\n  { max: 0.30, color: '#2563eb', name: 'Water' },\n  { max: 0.45, color: '#eab308', name: 'Sand' },\n  { max: 0.65, color: '#16a34a', name: 'Grass' },\n  { max: 0.80, color: '#15803d', name: 'Trees' },\n  { max: 1.00, color: '#78716c', name: 'Mountain' },\n];\n\nfunction getTerrainType(val) {\n  for (const t of TERRAIN) {\n    if (val <= t.max) return t;\n  }\n  return TERRAIN[TERRAIN.length - 1];\n}\n\n// Generate world\nseed = 42;\nlet world = Array.from({ length: WORLD_H }, () =>\n  Array.from({ length: WORLD_W }, () => seededRandom())\n);\n\n// Smooth\nfor (let pass = 0; pass < SMOOTH_PASSES; pass++) {\n  const next = world.map((row) => [...row]);\n  for (let y = 1; y < WORLD_H - 1; y++) {\n    for (let x = 1; x < WORLD_W - 1; x++) {\n      let sum = 0, count = 0;\n      for (let dy = -1; dy <= 1; dy++) {\n        for (let dx = -1; dx <= 1; dx++) {\n          sum += world[y + dy][x + dx];\n          count++;\n        }\n      }\n      next[y][x] = sum / count;\n    }\n  }\n  world = next;\n}\n\n// Camera\nconst camera = { x: 0, y: 0 };\nconst keys = {};\n\ndocument.addEventListener('keydown', (e) => { keys[e.key] = true; });\ndocument.addEventListener('keyup', (e) => { keys[e.key] = false; });\n\nfunction updateCamera() {\n  const speed = 3;\n  if (keys['ArrowLeft'] || keys['a']) camera.x -= speed;\n  if (keys['ArrowRight'] || keys['d']) camera.x += speed;\n  if (keys['ArrowUp'] || keys['w']) camera.y -= speed;\n  if (keys['ArrowDown'] || keys['s']) camera.y += speed;\n\n  const maxX = WORLD_W * TILE_SIZE - canvas.width;\n  const maxY = WORLD_H * TILE_SIZE - canvas.height;\n  camera.x = Math.max(0, Math.min(maxX, camera.x));\n  camera.y = Math.max(0, Math.min(maxY, camera.y));\n}\n\nfunction drawTiles() {\n  const startCol = Math.floor(camera.x / TILE_SIZE);\n  const startRow = Math.floor(camera.y / TILE_SIZE);\n  const endCol = Math.min(WORLD_W, startCol + Math.ceil(canvas.width / TILE_SIZE) + 1);\n  const endRow = Math.min(WORLD_H, startRow + Math.ceil(canvas.height / TILE_SIZE) + 1);\n\n  for (let r = startRow; r < endRow; r++) {\n    for (let c = startCol; c < endCol; c++) {\n      const terrain = getTerrainType(world[r][c]);\n      const px = c * TILE_SIZE - camera.x;\n      const py = r * TILE_SIZE - camera.y;\n      ctx.fillStyle = terrain.color;\n      ctx.fillRect(px, py, TILE_SIZE, TILE_SIZE);\n    }\n  }\n}\n\nfunction drawMinimap() {\n  const mx = canvas.width - MINIMAP_SIZE - MINIMAP_PAD;\n  const my = MINIMAP_PAD;\n  const scaleX = MINIMAP_SIZE / WORLD_W;\n  const scaleY = MINIMAP_SIZE / WORLD_H;\n\n  // Background\n  ctx.fillStyle = 'rgba(0,0,0,0.7)';\n  ctx.fillRect(mx - 2, my - 2, MINIMAP_SIZE + 4, MINIMAP_SIZE + 4);\n\n  // Tiles\n  for (let r = 0; r < WORLD_H; r++) {\n    for (let c = 0; c < WORLD_W; c++) {\n      const terrain = getTerrainType(world[r][c]);\n      ctx.fillStyle = terrain.color;\n      ctx.fillRect(mx + c * scaleX, my + r * scaleY, Math.ceil(scaleX), Math.ceil(scaleY));\n    }\n  }\n\n  // Camera viewport indicator\n  const vx = mx + (camera.x / TILE_SIZE) * scaleX;\n  const vy = my + (camera.y / TILE_SIZE) * scaleY;\n  const vw = (canvas.width / TILE_SIZE) * scaleX;\n  const vh = (canvas.height / TILE_SIZE) * scaleY;\n  ctx.strokeStyle = '#fff';\n  ctx.lineWidth = 1.5;\n  ctx.strokeRect(vx, vy, vw, vh);\n}\n\nfunction drawHUD() {\n  const col = Math.floor((camera.x + canvas.width / 2) / TILE_SIZE);\n  const row = Math.floor((camera.y + canvas.height / 2) / TILE_SIZE);\n  const terrain = (row >= 0 && row < WORLD_H && col >= 0 && col < WORLD_W)\n    ? getTerrainType(world[row][col]) : { name: '?' };\n\n  ctx.fillStyle = 'rgba(0,0,0,0.6)';\n  ctx.fillRect(8, canvas.height - 32, 280, 24);\n  ctx.fillStyle = '#ccc';\n  ctx.font = '12px monospace';\n  ctx.fillText(`Pos: (${col}, ${row})  Terrain: ${terrain.name}  [Arrow keys to pan]`, 14, canvas.height - 15);\n}\n\nfunction loop() {\n  ctx.fillStyle = '#0a0a0a';\n  ctx.fillRect(0, 0, canvas.width, canvas.height);\n  updateCamera();\n  drawTiles();\n  drawMinimap();\n  drawHUD();\n  requestAnimationFrame(loop);\n}\n\nwindow.addEventListener('resize', () => {\n  canvas.width = window.innerWidth;\n  canvas.height = window.innerHeight;\n});\n\nloop();\n</script>\n</body>\n</html>","agent_docs/README.md":"# Canvas Tilemap Renderer\n\nA procedurally generated tilemap with camera panning and minimap overlay.\n\n## Running\n\nOpen `tilemap.html` in any modern browser. Use arrow keys or WASD to pan the camera across the terrain.\n\n## Terrain Types\n\n| Color | Terrain |\n|-------|---------|\n| Blue | Water |\n| Yellow | Sand |\n| Green | Grass |\n| Dark Green | Trees |\n| Gray | Mountain |\n\n## Customization\n\nEdit the constants in the script:\n- `WORLD_W` / `WORLD_H` — world dimensions in tiles\n- `TILE_SIZE` — pixel size per tile\n- `seed` — change for different terrain layouts\n- `SMOOTH_PASSES` — more passes = smoother terrain"}}