← back to Model Arena

data/artifacts/1109202cb075/qwen3-14b.html

518 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Dragon's Slumber</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    html, body {
      margin: 0;
      padding: 0;
      overflow: hidden;
      background: #111;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    canvas {
      display: block;
      width: 100%;
      height: 100%;
      background: linear-gradient(to bottom, #1a1a1a, #2a2a2a);
      touch-action: none;
    }
    #ui {
      position: absolute;
      top: 10px;
      left: 10px;
      color: #eee;
      z-index: 10;
      background: rgba(0, 0, 0, 0.5);
      padding: 10px;
      border-radius: 8px;
      font-size: 16px;
    }
    #ui h1 {
      margin: 0 0 8px 0;
      font-size: 18px;
    }
    #ui .status {
      font-size: 14px;
      margin: 2px 0;
      padding: 2px 5px;
      border-radius: 4px;
      background: #2a2a2a;
      white-space: nowrap;
    }
    .success {
      color: #0f0;
    }
    .warning {
      color: #f90;
    }
    .error {
      color: #f00;
    }
    #ui button {
      margin: 4px 0;
      padding: 4px 8px;
      font-size: 14px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
    #ui button:focus {
      outline: 2px solid #0ff;
    }
  </style>
</head>
<body>
  <div id="ui">
    <h1>Dragon's Slumber</h1>
    <div class="status">Use arrow keys to move the shield</div>
    <div class="status">Press space to shoot</div>
    <div id="status" class="status"></div>
  </div>
  <canvas id="game"></canvas>
  <script>
    const canvas = document.getElementById('game');
    const ctx = canvas.getContext('2d');
    let width = canvas.width = window.innerWidth;
    let height = canvas.height = window.innerHeight;
    window.addEventListener('resize', () => {
      width = canvas.width = window.innerWidth;
      height = canvas.height = window.innerHeight;
      reset();
    });

    const scale = Math.min(width / 1440, height / 810);
    const scaleWidth = width / 1440;
    const scaleHeight = height / 810;

    const ui = document.getElementById('status');

    let keys = {};
    let mouse = { x: 0, y: 0 };
    let isMouseDown = false;
    let shieldColor = 'lightgreen';
    let shieldSize = 80;
    let shieldPos = { x: width / 2, y: height - 60 };
    let dragonEye = { open: 0, openSpeed: 0.02 };
    let dragonBreath = 0;
    let breathSpeed = 0.001;

    let ball = {
      x: width / 2,
      y: height - 30,
      radius: 8,
      vx: (Math.random() - 0.5) * 4,
      vy: -4,
      active: true
    };

    let bricks = [];
    let maxBricks = 100;
    let brickCount = maxBricks;

    let powerUps = [];
    let paintColor = 'orange';
    let canPaint = false;
    let paintCooldown = 0;
    let paintDuration = 300;

    function reset() {
      dragonEye.open = 0;
      dragonBreath = 0;
      ball.active = true;
      ball.x = width / 2;
      ball.y = height - 30;
      ball.vx = (Math.random() - 0.5) * 4;
      ball.vy = -4;
      bricks = [];
      brickCount = maxBricks;
      powerUps = [];
      canPaint = false;
      paintCooldown = 0;
      shieldPos.x = width / 2;
      shieldPos.y = height - 60;
    }

    function drawDragon() {
      ctx.save();
      ctx.fillStyle = 'rgb(30, 30, 30)';
      ctx.beginPath();
      ctx.moveTo(0, height / 2);
      ctx.lineTo(width, height / 2);
      ctx.lineTo(width, height * 0.8);
      ctx.lineTo(0, height * 0.8);
      ctx.closePath();
      ctx.fill();
      ctx.restore();

      // Dragon eyes
      ctx.save();
      ctx.translate(width * 0.4, height * 0.6);
      ctx.beginPath();
      ctx.arc(0, 0, 16, 0, Math.PI * 2);
      ctx.fillStyle = 'gray';
      ctx.fill();
      ctx.beginPath();
      ctx.arc(0, 0, 5, 0, Math.PI * 2);
      ctx.fillStyle = 'black';
      ctx.fill();
      ctx.restore();

      ctx.save();
      ctx.translate(width * 0.6, height * 0.6);
      ctx.beginPath();
      ctx.arc(0, 0, 16, 0, Math.PI * 2);
      ctx.fillStyle = 'gray';
      ctx.fill();
      ctx.beginPath();
      ctx.arc(0, 0, 5, 0, Math.PI * 2);
      ctx.fillStyle = 'black';
      ctx.fill();
      ctx.restore();

      // Dragon eye opening
      ctx.save();
      ctx.translate(width * 0.4, height * 0.6);
      ctx.beginPath();
      ctx.arc(0, 0, 5 * dragonEye.open, 0, Math.PI * 2);
      ctx.fillStyle = 'white';
      ctx.fill();
      ctx.restore();

      ctx.save();
      ctx.translate(width * 0.6, height * 0.6);
      ctx.beginPath();
      ctx.arc(0, 0, 5 * dragonEye.open, 0, Math.PI * 2);
      ctx.fillStyle = 'white';
      ctx.fill();
      ctx.restore();

      // Dragon breath
      ctx.save();
      ctx.globalAlpha = dragonBreath;
      ctx.translate(width / 2, height * 0.65);
      ctx.fillStyle = 'rgba(255, 200, 0, 0.5)';
      ctx.beginPath();
      ctx.arc(0, 0, 30, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();

      // Dragon scales
      for (let i = 0; i < bricks.length; i++) {
        const brick = bricks[i];
        ctx.save();
        ctx.translate(brick.x, brick.y);
        ctx.fillStyle = brick.color;
        ctx.fillRect(-brick.w / 2, -brick.h / 2, brick.w, brick.h);
        ctx.restore();
      }

      // Power ups
      for (let i = 0; i < powerUps.length; i++) {
        const power = powerUps[i];
        ctx.save();
        ctx.translate(power.x, power.y);
        ctx.fillStyle = 'gold';
        ctx.beginPath();
        ctx.arc(0, 0, 10, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();
      }
    }

    function drawShield() {
      ctx.save();
      ctx.translate(shieldPos.x, shieldPos.y);
      ctx.fillStyle = shieldColor;
      ctx.beginPath();
      ctx.moveTo(-shieldSize / 2, -shieldSize / 2);
      ctx.lineTo(shieldSize / 2, -shieldSize / 2);
      ctx.lineTo(shieldSize / 2, shieldSize / 2);
      ctx.lineTo(-shieldSize / 2, shieldSize / 2);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }

    function drawBall() {
      if (!ball.active) return;
      ctx.beginPath();
      ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
      ctx.fillStyle = 'white';
      ctx.fill();
    }

    function update() {
      // Handle keys
      if (keys['ArrowLeft'] || keys['a']) {
        shieldPos.x -= 6;
      }
      if (keys['ArrowRight'] || keys['d']) {
        shieldPos.x += 6;
      }
      if (keys['ArrowUp'] || keys['w']) {
        shieldPos.y -= 6;
      }
      if (keys['ArrowDown'] || keys['s']) {
        shieldPos.y += 6;
      }

      if (keys[' ']) {
        if (ball.active) {
          keys[' '] = false;
          shootBall();
        }
      }

      // Clamp shield
      shieldPos.x = Math.max(shieldSize / 2, Math.min(width - shieldSize / 2, shieldPos.x));
      shieldPos.y = Math.max(shieldSize / 2, Math.min(height - shieldSize / 2, shieldPos.y));

      // Update ball
      if (ball.active) {
        ball.x += ball.vx;
        ball.y += ball.vy;

        // Wall collision
        if (ball.x < ball.radius || ball.x > width - ball.radius) {
          ball.vx *= -1;
        }
        if (ball.y < ball.radius) {
          ball.vy *= -1;
        }

        // Shield collision
        const dx = ball.x - shieldPos.x;
        const dy = ball.y - shieldPos.y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < shieldSize / 2 + ball.radius) {
          const angle = Math.atan2(dy, dx);
          const hitX = Math.cos(angle) * (shieldSize / 2 + ball.radius);
          const hitY = Math.sin(angle) * (shieldSize / 2 + ball.radius);
          ball.x = shieldPos.x + hitX;
          ball.y = shieldPos.y + hitY;

          const normalX = Math.cos(angle);
          const normalY = Math.sin(angle);
          const dot = (ball.vx * normalX + ball.vy * normalY);
          const reflectX = ball.vx - 2 * dot * normalX;
          const reflectY = ball.vy - 2 * dot * normalY;
          ball.vx = reflectX;
          ball.vy = reflectY;
        }

        // Brick collisions
        for (let i = 0; i < bricks.length; i++) {
          const brick = bricks[i];
          const dx = ball.x - brick.x;
          const dy = ball.y - brick.y;
          const dist = Math.sqrt(dx * dx + dy * dy);
          if (dist < brick.radius + ball.radius) {
            const angle = Math.atan2(dy, dx);
            const hitX = Math.cos(angle) * (brick.radius + ball.radius);
            const hitY = Math.sin(angle) * (brick.radius + ball.radius);
            ball.x = brick.x + hitX;
            ball.y = brick.y + hitY;

            const normalX = Math.cos(angle);
            const normalY = Math.sin(angle);
            const dot = (ball.vx * normalX + ball.vy * normalY);
            const reflectX = ball.vx - 2 * dot * normalX;
            const reflectY = ball.vy - 2 * dot * normalY;
            ball.vx = reflectX;
            ball.vy = reflectY;

            bricks.splice(i, 1);
            brickCount--;
            i--;

            if (brickCount === 0) {
              // Dragon wakes up
              if (dragonEye.open < 1) {
                dragonEye.open += 0.1;
              }
              if (dragonBreath < 1) {
                dragonBreath += 0.01;
              }
            } else {
              // Dragon is still sleeping
              if (dragonEye.open > 0) {
                dragonEye.open -= 0.05;
              }
              if (dragonBreath > 0) {
                dragonBreath -= 0.005;
              }
            }
          }
        }

        // Power-up collisions
        for (let i = 0; i < powerUps.length; i++) {
          const power = powerUps[i];
          const dx = ball.x - power.x;
          const dy = ball.y - power.y;
          const dist = Math.sqrt(dx * dx + dy * dy);
          if (dist < 10 + ball.radius) {
            powerUps.splice(i, 1);
            canPaint = true;
            paintCooldown = paintDuration;
            i--;
          }
        }

        // Game over
        if (ball.y > height) {
          ball.active = false;
          ui.textContent = 'Game Over! Press R to restart.';
          ui.className = 'status error';
        }
      }

      // Update paint cooldown
      if (paintCooldown > 0) {
        paintCooldown -= 1;
      }

      // Update dragon eye and breath
      if (dragonEye.open < 1) {
        dragonEye.open += dragonEye.openSpeed;
      }
      if (dragonBreath < 1) {
        dragonBreath += breathSpeed;
      }
    }

    function draw() {
      ctx.clearRect(0, 0, width, height);
      drawDragon();
      drawShield();
      drawBall();

      // UI status
      if (brickCount === 0) {
        ui.textContent = 'Dragon Awakened!';
        ui.className = 'status success';
      } else if (brickCount < 5) {
        ui.textContent = 'Dragon Stirring!';
        ui.className = 'status warning';
      } else {
        ui.textContent = `${brickCount} scales left.`;
        ui.className = 'status';
      }

      if (canPaint) {
        ui.innerHTML += '<div class="status success">You can paint a new scale! (Press P)</div>';
      } else {
        ui.innerHTML = '';
      }
    }

    function shootBall() {
      if (!ball.active) {
        ball.active = true;
        ball.x = shieldPos.x;
        ball.y = shieldPos.y - shieldSize / 2 - ball.radius;
        ball.vx = (Math.random() - 0.5) * 4;
        ball.vy = -4;
      }
    }

    function startGame() {
      reset();
      generateBricks();
      generatePowerUps();
    }

    function generateBricks() {
      bricks = [];
      brickCount = maxBricks;
      for (let i = 0; i < maxBricks; i++) {
        const w = 40 + Math.random() * 20;
        const h = 20 + Math.random() * 10;
        const x = Math.random() * (width - w);
        const y = (height / 2) * Math.random();
        bricks.push({
          x: x,
          y: y,
          w: w,
          h: h,
          radius: Math.sqrt((w / 2) ** 2 + (h / 2) ** 2),
          color: `hsl(${Math.random() * 360}, 80%, 60%)`
        });
      }
    }

    function generatePowerUps() {
      powerUps = [];
      for (let i = 0; i < Math.floor(maxBricks / 10); i++) {
        const x = Math.random() * width;
        const y = Math.random() * height;
        powerUps.push({ x: x, y: y });
      }
    }

    function handleInput(e) {
      if (e.type === 'keydown') {
        keys[e.key] = true;
        if (e.key === 'r' || e.key === 'R') {
          startGame();
        }
        if (e.key === 'p' || e.key === 'P') {
          if (canPaint) {
            const x = shieldPos.x;
            const y = shieldPos.y - shieldSize / 2 - 30;
            const w = 40 + Math.random() * 20;
            const h = 20 + Math.random() * 10;
            bricks.push({
              x: x,
              y: y,
              w: w,
              h: h,
              radius: Math.sqrt((w / 2) ** 2 + (h / 2) ** 2),
              color: paintColor
            });
            brickCount++;
            canPaint = false;
            paintCooldown = 0;
          }
        }
      } else if (e.type === 'keyup') {
        keys[e.key] = false;
      }
    }

    function init() {
      startGame();
      window.addEventListener('keydown', handleInput);
      window.addEventListener('keyup', handleInput);
      window.addEventListener('mousedown', (e) => {
        isMouseDown = true;
        mouse.x = e.clientX;
        mouse.y = e.clientY;
      });
      window.addEventListener('mouseup', () => isMouseDown = false);
      window.addEventListener('mousemove', (e) => {
        mouse.x = e.clientX;
        mouse.y = e.clientY;
      });

      let lastTime = 0;
      function loop(time) {
        const deltaTime = time - lastTime;
        lastTime = time;

        update();
        draw();

        requestAnimationFrame(loop);
      }

      requestAnimationFrame(loop);
    }

    init();
  </script>
</body>
</html>