AI Code Test

Sonic

Review the original prompt, inspect the generated code, and compare how different AI models handled the same coding task.

Back to All Tests

Sonic

Gemini Flash 3.5 (Standard)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sonic-Inspired Physics Block Test</title>
    <style>
        body {
            margin: 0;
            background: #111;
            color: #fff;
            font-family: sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            overflow: hidden;
        }
        canvas {
            border: 4px solid #333;
            background: #223355;
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
        }
        .ui {
            margin-top: 10px;
            font-size: 14px;
            text-align: center;
            line-height: 1.5;
        }
    </style>
</head>
<body>

<canvas id="gameCanvas" width="800" height="480"></canvas>
<div class="ui">
    <strong>Controls:</strong> Left/Right Arrow = Run | Space = Jump | Down Arrow = Roll <br>
    <span id="stats">Speed: 0 | Rolling: False</span>
</div>

<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const stats = document.getElementById('stats');

// --- Physics Constants (Inspired by Sonic 1 Specs) ---
const ACCEL = 0.08;
const DECEL = 0.5;
const FRICTION = 0.046;
const MAX_SPEED = 8;
const JUMP_FORCE = -8.5;
const GRAVITY = 0.35;
const ROLL_FRICTION = 0.023;
const ROLL_DECEL = 0.25;

// --- Player Object ---
const player = {
    x: 100,
    y: 300,
    w: 24,
    h: 36,
    vx: 0,
    vy: 0,
    isGrounded: false,
    isRolling: false,
    color: '#0055ff' // Sonic Blue
};

// --- Level Data (Simple Blocks/Platforms) ---
const platforms = [
    // Main Floor
    { x: 0, y: 420, w: 1200, h: 60, color: '#33aa33' },
    // Step Blocks
    { x: 300, y: 360, w: 80, h: 60, color: '#aa6633' },
    { x: 380, y: 300, w: 80, h: 120, color: '#aa6633' },
    // High Platform to jump off of
    { x: 550, y: 220, w: 200, h: 20, color: '#aa6633' },
    // Long flat stretch for building top speed
    { x: 850, y: 320, w: 400, h: 100, color: '#33aa33' }
];

// --- Input Tracking ---
const keys = { Left: false, Right: false, Down: false };

window.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowLeft' || e.key === 'a') keys.Left = true;
    if (e.key === 'ArrowRight' || e.key === 'd') keys.Right = true;
    if (e.key === 'ArrowDown' || e.key === 's') keys.Down = true;
    if (e.key === ' ' && player.isGrounded) {
        player.vy = JUMP_FORCE;
        player.isGrounded = false;
        if (player.isRolling) {
            // Keep rolling size if already rolling
        }
    }
});

window.addEventListener('keyup', (e) => {
    if (e.key === 'ArrowLeft' || e.key === 'a') keys.Left = false;
    if (e.key === 'ArrowRight' || e.key === 'd') keys.Right = false;
    if (e.key === 'ArrowDown' || e.key === 's') keys.Down = false;
});

// --- Simple AABB Collision Detection & Resolution ---
function checkCollision(p, plat) {
    return p.x < plat.x + plat.w &&
           p.x + p.w > plat.x &&
           p.y < plat.y + plat.h &&
           p.y + p.h > plat.y;
}

// --- Main Game Loop ---
function update() {
    // 1. Handle Rolling State Changes
    if (keys.Down && player.isGrounded && Math.abs(player.vx) > 1.0 && !player.isRolling) {
        player.isRolling = true;
        player.h = 24; // Compress hitbox into a "ball"
        player.y += 12; 
        player.color = '#00bbff'; // Lighter spin blue
    }
    
    if (player.isRolling && (Math.abs(player.vx) < 0.2 || !player.isGrounded || (!keys.Down && Math.abs(player.vx) < 1.0))) {
        // Unroll if stopped, airborne, or letting go of down at slow speeds
        if (player.isRolling && player.isGrounded) {
            player.isRolling = false;
            player.h = 36;
            player.y -= 12;
            player.color = '#0055ff';
        }
    }

    // 2. Horizontal Movement Mechanics (Acceleration, Deceleration, Friction)
    let currentDrag = player.isRolling ? ROLL_FRICTION : FRICTION;
    
    if (keys.Left) {
        if (player.vx > 0) {
            player.vx -= player.isRolling ? ROLL_DECEL : DECEL;
        } else if (player.vx > -MAX_SPEED) {
            player.vx -= player.isRolling ? 0 : ACCEL; // Sonic can't actively accelerate well while rolling
        }
    } else if (keys.Right) {
        if (player.vx < 0) {
            player.vx += player.isRolling ? ROLL_DECEL : DECEL;
        } else if (player.vx < MAX_SPEED) {
            player.vx += player.isRolling ? 0 : ACCEL;
        }
    } else {
        // Apply friction when no direction is pressed
        if (player.vx > 0) player.vx = Math.max(0, player.vx - currentDrag);
        if (player.vx < 0) player.vx = Math.min(0, player.vx + currentDrag);
    }

    // Apply X position
    player.x += player.vx;

    // Handle X collision resolution
    for (let plat of platforms) {
        if (checkCollision(player, plat)) {
            if (player.vx > 0) {
                player.x = plat.x - player.w;
                player.vx = 0; // Wall bonk
            } else if (player.vx < 0) {
                player.x = plat.x + plat.w;
                player.vx = 0;
            }
        }
    }

    // 3. Vertical Movement Mechanics (Gravity & Jumping)
    if (!player.isGrounded) {
        player.vy += GRAVITY;
    }
    
    // Apply Y position
    player.y += player.vy;
    player.isGrounded = false;

    // Handle Y collision resolution
    for (let plat of platforms) {
        if (checkCollision(player, plat)) {
            // Landing on top of a platform
            if (player.vy > 0 && (player.y + player.h - player.vy) <= plat.y + 4) {
                player.y = plat.y - player.h;
                player.vy = 0;
                player.isGrounded = true;
            } 
            // Bonking ceiling
            else if (player.vy < 0) {
                player.y = plat.y + plat.h;
                player.vy = 0;
            }
        }
    }

    // Update debug text
    stats.innerText = `Speed: ${Math.abs(player.vx).toFixed(2)} | Rolling: ${player.isRolling} | Grounded: ${player.isGrounded}`;
}

function draw() {
    // Clear Screen & Apply Camera Tracking (Offsets view based on player x)
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    ctx.save();
    // Simple camera tracking centered around player X
    let cameraX = -player.x + canvas.width / 2 - player.w / 2;
    // Constrain camera boundary to level width
    cameraX = Math.min(0, cameraX);
    ctx.translate(cameraX, 0);

    // Draw Platforms
    for (let plat of platforms) {
        ctx.fillStyle = plat.color;
        ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
        
        // Simple checkered grid look on platforms to show movement speed
        ctx.fillStyle = 'rgba(0,0,0,0.05)';
        for (let i = 0; i < plat.w; i += 20) {
            if ((i / 20) % 2 === 0) {
                ctx.fillRect(plat.x + i, plat.y, 20, plat.h);
            }
        }
    }

    // Draw Player
    ctx.fillStyle = player.color;
    if (player.isRolling) {
        // Draw spinning ball block approximation
        ctx.fillRect(player.x, player.y, player.w, player.h);
        ctx.fillStyle = '#fff';
        ctx.fillRect(player.x + 4, player.y + 4, 6, 6); // Core spin accent
    } else {
        // Normal running block
        ctx.fillRect(player.x, player.y, player.w, player.h);
        // "Shoes" accent line
        ctx.fillStyle = '#ff0000';
        ctx.fillRect(player.x, player.y + player.h - 4, player.w, 4);
    }

    ctx.restore();
}

function loop() {
    update();
    draw();
    requestAnimationFrame(loop);
}

// Start the game loop
loop();
</script>

</body>
</html>

Models

72
Compare code outputs