OverviewCode › R94
Code · blind round

R94 · Raycast maze walkthrough

blind round 3/8 local models answered

Answers below are anonymized and shuffled. Read, judge, pick a favourite — then scroll down and reveal the models.

The field: deepseek-r1:14b · gemma4:26b · llama3.1:8b · mistral-small:24b · qwen3-coder:30b · qwen3:14b · qwen3:8b · qwen3.5:9b — listed alphabetically, which is not the order of the cards below.

2 of the 8 local models delivered nothing through our test harness in this round — for reasons that lie with our setup, not with the models. Those cards are marked DNF like any other, but they are not evidence about those models. Which ones, and why, is spelled out in the reveal.

Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 7000 characters in total — this is a budget on characters, not on lines, so removing line breaks does not help) that renders a first-person view of a grid maze by raycasting on a <canvas>: cast one ray per screen column against a fixed wall grid, take the distance to the first wall hit, correct it against the fisheye effect, and draw a vertical wall slice whose height and shading follow that distance. Move the camera along the corridors and turn at junctions on its own, without any user input. Show the camera position and heading. Use only the 2D canvas context — no WebGL, no libraries. Smooth requestAnimationFrame animation, NO user input, starts immediately and keeps running (restart with a fresh random instance when finished). No external libraries, no comments needed. Output ONLY the HTML file content, nothing else.

The answers — blind

Local models (Brain cluster) — identical prompt, anonymized order
MODEL Aqwen3-coder:30bBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Autonomous Raycaster Maze</title>
    <style>
        body { margin: 0; background: #111; display: flex; justify-content: center; align-items: center; height: 100vh; }
        canvas { border: 1px solid #333; background: #000; }
    </style>
</head>
<body>
    <canvas id="c"></canvas>
    <script>
        const c = document.getElementById('c');
        const ctx = c.getContext('2d');

        // --- Configuration ---
        const MAP_SIZE = 10;
        const CELL_SIZE = 64;
        const SCREEN_W = 800;
        const SCREEN_H = 600;

        c.width = SCREEN_W;
        c.height = SCREEN_H;

        // --- Game State Variables ---
        let maze = [];
        let player = { x: 0, y: 0, angle: Math.PI / 2 }; // Start facing positive Y (down)
        const MOVEMENT_SPEED = 0.15;
        const ANGULAR_SPEED = 0.03;

        // --- Maze Generation (Recursive Backtracker) ---
        function generateMaze() {
            let grid = Array(MAP_SIZE).fill(0).map(() => Array(MAP_SIZE).fill(true)); // True means wall exists
            maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
                    [1, 0, 1, 0, 0, 0, 0, 1, 0, 1],
                    [1, 0, 1, 0, 1, 1, 0, 1, 0, 1],
                    [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
                    [1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 1, 1, 1, 1, 1, 1, 1, 1]
                   ];

            let stack = [];
            let start_x = 1;
            let start_y = 1;

            stack.push({ x: start_x, y: start_y });
            grid[start_y][start_x] = false; // Clear starting cell

            while (stack.length > 0) {
                let current = stack[stack.length - 1];
                let neighbors = [];

                // Check potential moves (N, S, E, W)
                const dirs = [[0, -2], [0, 2], [-2, 0], [2, 0]];
                for (const [dx, dy] of dirs) {
                    let nx = current.x + dx;
                    let ny = current.y + dy;

                    if (nx >= 0 && nx < MAP_SIZE && ny >= 0 && ny < MAP_SIZE && 
                        !grid[ny][nx] && grid[current.y + Math.floor((dy / 2))][current.x + Math.floor((dx / 2))] === true) {
                        neighbors.push({ x: nx, y: ny, wall_to_clear: [current.x + dx / 2, current.y + dy / 2] });
                    }
                }

                if (neighbors.length > 0) {
                    let next = neighbors[Math.floor(Math.random() * neighbors.length)];
                    
                    // Clear the wall between current and next
                    grid[current.y + Math.floor((next.y - current.y) / 2)][current.x + Math.floor((next.x - current.x) / 2)] = false;

                    grid[next.y][next.x] = false;
                    stack.push({ x: next.x, y: next.y });
                } else {
                    stack.pop();
                }
            }
        }


        // --- Raycasting Core Logic ---
        function castRay(angle) {
            const MAX_DIST = 10 * CELL_SIZE;
            let x = player.x + Math.cos(angle) * 0.1;
            let y = player.y + Math.sin(angle) * 0.1;

            let sinA = Math.sin(angle);
            let cosA = Math.cos(angle);

            const step = 0.005; // Step size for ray increments
            let distance = 0;

            while (distance < MAX_DIST) {
                x += cosA * step;
                y += sinA * step;
                distance += step;

                // Check map collision using integer coordinates of the grid cell edge
                const gx = Math.floor(x);
                const gy = Math.floor(y);

                if (gx < 0 || gx >= MAP_SIZE || gy < 0 || gy >= MAP_SIZE) break;

                // Check if the point (gx, gy) is a wall (1)
                let currentCellWall = maze[gy][gx];

                if (currentCellWall === 1) {
                    // Collision detected. Distance calculation needs refinement for true hit spot.
                    // For simplicity and performance in this limited context, we use the distance accumulated so far.
                    return Math.sqrt((x - player.x)**2 + (y - player.y)**2);
                }
            }

            return MAX_DIST; // Never hits a wall
        }

        function drawScene() {
            ctx.clearRect(0, 0, SCREEN_W, SCREEN_H);

            // Draw floor/ceiling (simple ambient color)
            ctx.fillStyle = '#444'; ctx.fillRect(0, 0, SCREEN_W, SCREEN_H / 2); // Ceiling
            ctx.fillStyle = '#666'; ctx.fillRect(0, SCREEN_H / 2, SCREEN_W, SCREEN_H / 2); // Floor

            // Raycasting loop (one ray per vertical screen slice)
            const numRays = SCREEN_W;
            for (let i = 0; i < numRays; i++) {
                const angle = player.angle - Math.PI / 2 + (i * (Math.PI / numRays));

                // Angle correction: Raycasting works better if the angle is relative to the X-axis
                let rayAngle = angle;

                // Distance calculation using simple coordinate projection for performance
                let distance = castRay(rayAngle);

                // Fisheye Correction (Projected Horizontal Distance)
                const correctedDistance = distance * Math.cos(Math.abs(player.angle - rayAngle));

                if (correctedDistance < 1) continue; // Ignore if too close/invalid

                // Calculate wall height and position on screen
                const wallHeight = SCREEN_H / (correctedDistance / CELL_SIZE);
                const drawStart = Math.max(0, (SCREEN_H - wallHeight) / 2);
                const drawEnd = Math.min(SCREEN_H, drawStart + wallHeight);

                // Shading (simple inverse function based on distance)
                let shadeStrength = 1 - Math.min(1, correctedDistance / (CELL_SIZE * 2));
                let colorFactor = Math.floor(shadeStrength * 150);
                
                ctx.fillStyle = `rgb(${colorFactor}, ${colorFactor}, ${colorFactor})`;
                ctx.fillRect(i, drawStart, 1, drawEnd - drawStart);
            }

            // Display Player Info (HUD)
            ctx.fillStyle = 'white';
            ctx.font = '16px monospace';
            ctx.fillText(`Pos: (${Math.floor(player.x)}, ${Math.floor(player.y)})`, 10, 20);
            ctx.fillText(`Angle: ${(player.angle * 180 / Math.PI).toFixed(0)}°`, 10, 40);
        }

        // --- Movement and AI Logic ---

        function movePlayer() {
            let newX = player.x + Math.cos(player.angle) * MOVEMENT_SPEED;
            let newY = player.y + Math.sin(player.angle) * MOVEMENT_SPEED;

            // Check collision (using integer grid coordinates for simplicity)
            const mapCheckX = Math.floor(newX);
            const mapCheckY = Math.floor(newY);

            if (mapCheckX >= 1 && mapCheckX < MAP_SIZE - 1 && 
                mapCheckY >= 1 && mapCheckY < MAP_SIZE - 1) {
                if (maze[Math.floor(newY)][Math.floor(newX)] === 0) {
                    player.x = newX;
                    player.y = newY;
                }
            }

            // Autonomous Turning Logic: Try to keep moving forward or turn slightly if blocked
            const rayDistance = castRay(player.angle);
            if (rayDistance > 4 * CELL_SIZE) { // If clear path ahead
                // Simple drift/jiggle random turning tendency
                if (Math.random() < 0.01) {
                    player.angle += (Math.random() - 0.5) * 0.2;
                }
            } else {
                let turned = false;
                // Check for potential turn options (neighbors in a circle around current cell)
                const directions = [
                    { dx: 1, dy: 0, angle_change: Math.PI / 2 },
                    { dx: -1, dy: 0, angle_change: -Math.PI / 2 },
                    { dx: 0, dy: 1, angle_change: 0 },
                    { dx: 0, dy: -1, angle_change: Math.PI }
                ];

                // Prioritize turning to the most open adjacent cell
                let bestTurn = null;
                let minDistanceToOpenCell = Infinity;

                for (const { dx, dy, angle_change } of directions) {
                    if (maze[Math.floor(player.y + dy)][Math.floor(player.x + dx)] === 0) {
                        // Calculate rough turning cost/benefit
                        let potentialAngle = player.angle + angle_change;
                        
                        // Check if this new direction is better than the current blocked path
                        if (potentialAngle !== player.angle && Math.random() < 0.1) {
                            bestTurn = { angle: potentialAngle, cost: 0 }; // Simple acceptance for now
                            break;
                        }
                    }
                }

                if (bestTurn) {
                    player.angle = bestTurn.angle;
MODEL Bqwen3.5:9bERRSTATFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head><title>Auto Maze Raycaster</title></head>
<body>
<canvas id="c" width="640" height="320"></canvas>
<script>
const C = document.getElementById('c');
const ctx = C.getContext('2d');

// --- Configuration ---
const MAP_W = 10;
const MAP_H = 8;
const TILE_SIZE = 64;
const WALL_THICKNESS = 1; // Assume 1 unit thick walls

// Maze: 1=Wall, 0=Path
let maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 1, 0, 1, 0, 1, 1, 0, 1],
    [1, 0, 1, 0, 0, 0, 0, 1, 0, 1],
    [1, 0, 1, 1, 1, 1, 0, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];

// Camera State
let cam = { x: TILE_SIZE * 1.5, y: TILE_SIZE * 1.5, angle: Math.PI / 2 }; // Start at (1.5, 1.5), facing South
const SECTOR_SIZE = 640;

// --- Utility Functions ---

function checkWall(x, y) {
    let mapX = Math.floor((x + TILE_SIZE / 2) / TILE_SIZE);
    let mapY = Math.floor((y + TILE_SIZE / 2) / TILE_SIZE);
    return maze[mapY][mapX] === 1;
}

function drawBackground() {
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, C.width, C.height);
}

// --- Raycasting Core ---

function castRay(angle) {
    const maxDist = 5000;
    let dx = Math.cos(angle) * 10;
    let dy = Math.sin(angle) * 10;
    let x = cam.x + dx * 0.1;
    let y = cam.y + dy * 0.1;
    let dist = 0;

    while (dist < maxDist && !checkWall(x, y)) {
        x += dx;
        y += dy;
        dist += Math.hypot(dx, dy);
    }
    return Math.min(dist, 500); // Cap distance for rendering
}

// Main Raycasting Loop
function renderScene() {
    drawBackground();

    const screenWidth = C.width;
    for (let i = 0; i < screenWidth; i++) {
        let rayAngle = cam.angle - Math.PI / 2 + (i / screenWidth) * Math.PI;

        // Simple distance calculation (Raycasting approximation for the center point of the column)
        let dist = castRay(rayAngle);
        
        // Height calculation and fisheye correction
        const projectedHeight = (C.width / 2) / (dist === 0 ? 1 : Math.cos(rayAngle - cam.angle));
        const actualHeight = 64 * (Math.cos(rayAngle - cam.angle)) / dist;

        // Draw wall slice
        let shade = 1 - Math.min(1, dist / 500); // Simple fading based on distance
        ctx.fillStyle = `rgb(${20 + 80 * shade}, ${20 + 80 * shade}, ${20 + 80 * shade})`;
        ctx.fillRect(i, (C.height - actualHeight) / 2, 1, actualHeight);

        // Optional: Draw floor/ceiling lines to visualize the effect
        /*
        ctx.fillStyle = 'green'; ctx.fillRect(i, C.height / 2 + 5, 1, 5);
        ctx.fillStyle = 'brown'; ctx.fillRect(i, 0, 1, 5);
        */
    }
}

// --- Movement and Logic ---

function moveCamera() {
    const speed = 3;
    const turnSpeed = Math.PI / 40; // radians per frame

    // 1. Attempt to move forward
    let newX = cam.x + Math.cos(cam.angle) * speed;
    let newY = cam.y + Math.sin(cam.angle) * speed;

    if (!checkWall(newX, cam.y)) {
        cam.x = newX;
        cam.y = newY;
    } else if (Math.abs(cam.x - newX) > 5 && Math.abs(cam.y - newY) > 5) {
        // If blocked, try turning instead of simply stopping
    }

    // 2. Check for junction/blocking and turn automatically
    let canTurn = false;
    const testAngles = [Math.PI / 2, Math.PI, -Math.PI / 2]; // Try N, W, S relative to current angle
    for (const offset of testAngles) {
        let testAngle = cam.angle + offset;
        let testX = cam.x + Math.cos(testAngle) * 50;
        let testY = cam.y + Math.sin(testAngle) * 50;

        if (!checkWall(testX, testY)) {
            canTurn = true;
            break;
        }
    }

    // Simple Auto-Turning Logic: If blocked and we can turn, turn slightly right (easiest heuristic)
    if (!checkWall(newX, cam.y) && !canTurn) {
         cam.angle += turnSpeed * 0.5; // Slow rotation if stuck
    } else if (Math.abs(cos(cam.angle - Math.PI/2)*3) < 0.1 && checkWall(newX, cam.y)) {
        // Simple collision detection: If move fails AND we can turn
        let testAngle = cam.angle + Math.PI / 4; // Try turning right slightly
        let testX = cam.x + Math.cos(testAngle) * 50;
        let testY = cam.y + Math.sin(testAngle) * 50;

        if (!checkWall(testX, testY)) {
            cam.angle += turnSpeed; // Turn right
        } else {
             // If turning also fails, just stop/jitter angle slightly
             cam.angle = cam.angle + (Math.random() - 0.5) * 0.1;
        }
    }


    // Update display
    ctx.fillStyle = 'white';
    ctx.font = "12px monospace";
    ctx.fillText(`Pos: (${cam.x.toFixed(0)}, ${cam.y.toFixed(0)})`, 10, 20);
    ctx.fillText(`Angle: ${(cam.angle * 180 / Math.PI).toFixed(0)} deg`, 10, 40);
}

// --- Main Loop ---
function gameLoop() {
    renderScene();
    moveCamera();
    requestAnimationFrame(gameLoop);
}

// Start the simulation
gameLoop();

</script>
</body>
</html>
```
MODEL Cllama3.1:8bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Egemma4:26bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Fqwen3:14bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Autonomous Raycasting Maze</title>
    <style>
        body { margin: 0; background: #111; display: flex; justify-content: center; align-items: center; height: 100vh; }
        canvas { border: 2px solid #333; image-rendering: pixelated; }
    </style>
</head>
<body>
    <canvas id="mazeCanvas"></canvas>
    <script>
        const canvas = document.getElementById('mazeCanvas');
        const ctx = canvas.getContext('2d');

        let W, H;
        W = 800;
        H = 600;
        canvas.width = W;
        canvas.height = H;

        // Maze constants
        const GRID_SIZE = 10; // Grid is N*N of cells (e.g., 20x20)
        const CELL_SIZE = 64;
        const MAZE_MAP = [];
        const TILE_SCALE = 1;

        // Player state
        let player = { x: 3.5 * CELL_SIZE, y: 3.5 * CELL_SIZE, dir: Math.PI / 2, heading: 0 };

        function generateMaze() {
            const maze = Array(GRID_SIZE).fill(0).map(() => Array(GRID_SIZE).fill(1));
            const stack = [];
            let startX = 1, startY = 1;
            maze[startY][startX] = 0;

            stack.push({ x: startX, y: startY });

            while (stack.length > 0) {
                const current = stack[stack.length - 1];
                const neighbors = [];
                for (let d = 0; d < 4; d++) {
                    let nx = current.x + Math.cos(d * Math.PI / 2) * 2;
                    let ny = current.y + Math.sin(d * Math.PI / 2) * 2;

                    if (nx >= 1 && nx < GRID_SIZE - 1 && ny >= 1 && ny < GRID_SIZE - 1 && maze[Math.floor(ny)][Math.floor(nx)] === 1) {
                        neighbors.push({ x: Math.floor(nx), y: Math.floor(ny) });
                    }
                }

                if (neighbors.length > 0) {
                    const next = neighbors[Math.floor(Math.random() * neighbors.length)];
                    const wallX = current.x + (next.x - current.x) / 2;
                    const wallY = current.y + (next.y - current.y) / 2;

                    maze[next.y][next.x] = 0;
                    maze[Math.floor(wallY)][Math.floor(wallX)] = 0; // Open path and connecting wall
                    stack.push({ x: next.x, y: next.y });
                } else {
                    stack.pop();
                }
            }
            return maze;
        }

        // Set the global maze map (using A* style coordinates for simplicity)
        function initializeMaze(mazeMap) {
             MAZE_MAP[Math.floor(player.y / CELL_SIZE)][Math.floor(player.x / CELL_SIZE)] = 0;
             for(let i=1; i<GRID_SIZE-1; i++) {
                 for(let j=1; j<GRID_SIZE-1; j++) {
                     MAZE_MAP[i][j] = mazeMap[Math.floor(i)][Math.floor(j)];
                 }
             }
        }

        function castRays() {
            ctx.clearRect(0, 0, W, H);
            const FOV = Math.PI / 3;
            const numRays = W;
            const angleStep = FOV / numRays;

            for (let i = 0; i < numRays; i++) {
                let rayAngle = player.heading - Math.PI/2 + i * angleStep; // Adjust starting angle for screen orientation

                // Simple grid-based raycasting check
                let x = player.x;
                let y = player.y;
                let dx = Math.cos(rayAngle);
                let dy = Math.sin(rayAngle);
                
                const maxDist = 20 * CELL_SIZE;
                let dist = 0;

                // Ray march (simple fixed step)
                while (dist < maxDist) {
                    x += dx * 1;
                    y += dy * 1;
                    dist++;

                    if(MAZE_MAP[Math.floor(y / CELL_SIZE)][Math.floor(x / CELL_SIZE)] === 1) {
                        break; // Hit a wall cell
                    }
                }

                // Correct distance for fisheye effect
                let correctedDist = dist * Math.cos(player.heading - rayAngle);
                if (correctedDist < 0.5) correctedDist = 0.5;

                const wallHeight = W / (correctedDist * Math.tan(FOV / 2));
                const shade = 1 - (Math.min(1, dist / maxDist));
                const color = `rgb(${50 + 50 * shade}, ${30 + 30 * shade}, ${40 + 30 * shade})`;

                ctx.fillStyle = color;
                ctx.fillRect(i * (W / numRays), 0, W / numRays, wallHeight);
            }
        }

        function moveCamera() {
            const speed = 5;
            const turnSpeed = Math.PI / 10;
            let moved = false;

            // Attempt Movement: Forward
            let nextX = player.x + Math.cos(player.heading) * speed;
            let nextY = player.y + Math.sin(player.heading) * speed;
            
            const checkCollision = (px, py) => {
                const gridX = Math.floor(px / CELL_SIZE);
                const gridY = Math.floor(py / CELL_SIZE);
                if (gridX < 0 || gridY < 0 || gridX >= GRID_SIZE || gridY >= GRID_SIZE) return true; // Out of bounds is a wall
                return MAZE_MAP[gridY][gridX] === 1;
            };

            if (!checkCollision(nextX, nextY)) {
                player.x = nextX;
                player.y = nextY;
                moved = true;
            }

            // Autonomous Turning Logic (Attempt to turn if blocked)
            if (!moved) {
                let attempts = [Math.PI/2, -Math.PI/2]; // Try right and left first
                for(const angleChange of attempts) {
                    player.heading += angleChange * 0.5; // Small rotation step
                    player.heading = player.heading % (2 * Math.PI);

                    // Check if the new direction allows movement
                    nextX = player.x + Math.cos(player.heading) * speed * 0.1;
                    nextY = player.y + Math.sin(player.heading) * speed * 0.1;
                    if (!checkCollision(nextX, nextY)) {
                        // Successfully turned and can move slightly
                        player.x += Math.cos(player.heading) * speed * 0.5;
                        player.y += Math.sin(player.heading) * speed * 0.5;
                        break; // Exit turn loop
                    }
                }
            }

            // Draw Debug Info (Position and Heading)
            ctx.fillStyle = '#FFF';
            ctx.fillText(`Pos: (${Math.floor(player.x/CELL_SIZE)}, ${Math.floor(player.y/CELL_SIZE)})`, 10, H - 20);
            ctx.fillText(`Dir: ${(player.heading / Math.PI).toFixed(1) * 180} deg`, 10, H - 5);
        }

        function animate() {
            moveCamera();
            castRays();
            requestAnimationFrame(animate);
        }

        // --- Initialization Sequence ---
        const maze = generateMaze();
        initializeMaze(maze);
        animate();

    </script>
</body>
</html>
```
MODEL Gdeepseek-r1:14bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Hqwen3:8bDNF
No usable answer reached the harness (ERR). Counted as a did-not-finish.
Reveal the models— compare the answers first, then open. Names, blind scores and latencies are hidden until you do.

Who wrote what

Blind labelModelBlind scoreLatencyStatus
MODEL A qwen3-coder:30b 38.7 sOK
MODEL B qwen3.5:9b 67.0 sOK
MODEL C llama3.1:8b DNF
MODEL D mistral-small:24b DNF
MODEL E gemma4:26b harness
MODEL F qwen3:14b 71.7 sOK
MODEL G deepseek-r1:14b DNF
MODEL H qwen3:8b harness