Pong för tanter

<!DOCTYPE html> <html> <head> <title>Pong från AINNOVA</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <style> body { margin: 0; padding: 10px; background: #f0f4f8; font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; touch-action: manipulation; overflow: hidden; } .game-container { position: relative; width: 100%; max-width: 600px; display: flex; flex-direction: column; align-items: center; } .logo-small { width: 80px; height: auto; margin-bottom: 5px; } canvas { border: 2px solid #007BBF; background: #000; width: 100%; max-height: 70vh; touch-action: none; } #startScreen { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(255, 255, 255, 0.95); padding: 15px; border-radius: 8px; text-align: center; width: 90%; max-width: 400px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); z-index: 10; } .button-container { display: flex; flex-direction: column; width: 100%; margin-top: 10px; } button { padding: 10px; margin: 5px 0; background: #007BBF; color: white; border: none; border-radius: 5px; font-size: 16px; width: 100%; cursor: pointer; } .score-display { font-size: 20px; margin: 5px 0; font-weight: bold; color: #007BBF; } h2 { font-size: 18px; margin: 5px 0; color: #333; text-align: center; } #status { color: #007BBF; font-size: 16px; margin-top: 5px; text-align: center; } @media (min-width: 400px) { .button-container { flex-direction: row; justify-content: center; } button { margin: 0 5px; width: auto; min-width: 120px; } } </style> </head> <body> <div class="game-container"> <img class="logo-small" src="https://valingeveckan.wordpress.com/wp-content/uploads/2025/04/namnlos-design-4-1.png" alt="AINNOVA Logo"> <h2>Pong – En demo från AINNOVA</h2> <div class="score-display" id="scoreDisplay">0 - 0</div> <canvas id="pong"></canvas> <div id="status"></div> <div id="startScreen"> <h1>Välkommen till Pong!</h1> <p>På dator: Använd W/S eller upp/ner-pil för vänster paddel.<br> På mobil: Tryck och dra upp/ner på vänster sida.<br> Två spelare? Höger paddel använder I/K (dator) eller dra på höger sida.<br> Mot datorn? Din paddel är större för att göra det lättare!<br> Första till 5 poäng vinner!</p> <div class="button-container"> <button onclick="startGame(false)">2 Spelare</button> <button onclick="startGame(true)">Mot datorn</button> </div> </div> </div>

<script> const canvas = document.getElementById('pong'); const ctx = canvas.getContext('2d'); const startScreen = document.getElementById('startScreen'); const scoreDisplay = document.getElementById('scoreDisplay'); const status = document.getElementById('status');

// Spelinställningar
const PADDLE_WIDTH = 10;
const WINNING_SCORE = 5;
let scale = 1;

// Spelobjekt
let leftPaddle = { x: 0, y: 160, height: 80, speed: 5 };
let rightPaddle = { x: 0, y: 170, height: 60, speed: 4 };
let ball = { x: 300, y: 200, size: 8, dx: 4, dy: 4 };
let leftScore = 0, rightScore = 0;
let gameState = 'start';
let vsComputer = false;
let animationFrameId = null;
let lastTime = 0;
const frameRate = 60;
const frameDelay = 1000 / frameRate;

// Initiera spelet
function initGame() {
  setCanvasSize();
  window.addEventListener('resize', setCanvasSize);

  // Tangentbordsstyrning
  document.addEventListener('keydown', function(e) {
    e.preventDefault();
    const key = e.key.toLowerCase();
    if (key === 'w' || key === 'arrowup') keys.up = true;
    if (key === 's' || key === 'arrowdown') keys.down = true;
    if (key === 'i') keys.i = true;
    if (key === 'k') keys.k = true;
    if (key === ' ' && gameState === 'playing') {
      gameState = 'paused';
      status.textContent = 'Pausad – tryck mellanslag eller skärmen för att fortsätta';
    } else if (key === ' ' && gameState === 'paused') {
      gameState = 'playing';
      status.textContent = 'Spelet är igång!';
      gameLoop(performance.now());
    }
  });

  document.addEventListener('keyup', function(e) {
    const key = e.key.toLowerCase();
    if (key === 'w' || key === 'arrowup') keys.up = false;
    if (key === 's' || key === 'arrowdown') keys.down = false;
    if (key === 'i') keys.i = false;
    if (key === 'k') keys.k = false;
  });

  // Touchkontroller
  canvas.addEventListener('touchstart', handleTouch);
  canvas.addEventListener('touchmove', handleTouch);
  canvas.addEventListener('touchend', e => e.preventDefault());
  canvas.addEventListener('mousedown', handleMouse);
  canvas.addEventListener('mousemove', handleMouse);
}

let keys = { up: false, down: false, i: false, k: false };

function handleTouch(e) {
  e.preventDefault();
  if (gameState === 'paused') {
    gameState = 'playing';
    status.textContent = 'Spelet är igång!';
    gameLoop(performance.now());
    return;
  }

  const touch = e.touches[0] || e.changedTouches[0];
  const rect = canvas.getBoundingClientRect();
  if (rect.width === 0 || rect.height === 0) return; // Undvik division med noll
  const touchX = (touch.clientX - rect.left) / rect.width * canvas.width;
  const touchY = (touch.clientY - rect.top) / rect.height * canvas.height;

  if (touchX < canvas.width / 2) {
    leftPaddle.y = touchY - leftPaddle.height / 2;
  } else if (!vsComputer) {
    rightPaddle.y = touchY - rightPaddle.height / 2;
  }

  leftPaddle.y = Math.max(0, Math.min(canvas.height - leftPaddle.height, leftPaddle.y));
  rightPaddle.y = Math.max(0, Math.min(canvas.height - rightPaddle.height, rightPaddle.y));
}

function handleMouse(e) {
  if (gameState !== 'playing') return;

  const rect = canvas.getBoundingClientRect();
  if (rect.width === 0 || rect.height === 0) return; // Undvik division med noll
  const mouseX = (e.clientX - rect.left) / rect.width * canvas.width;
  const mouseY = (e.clientY - rect.top) / rect.height * canvas.height;

  if (mouseX < canvas.width / 2) {
    leftPaddle.y = mouseY - leftPaddle.height / 2;
  } else if (!vsComputer) {
    rightPaddle.y = mouseY - rightPaddle.height / 2;
  }

  leftPaddle.y = Math.max(0, Math.min(canvas.height - leftPaddle.height, leftPaddle.y));
  rightPaddle.y = Math.max(0, Math.min(canvas.height - rightPaddle.height, rightPaddle.y));
}

function setCanvasSize() {
  const maxWidth = Math.min(window.innerWidth - 20, 600);
  const maxHeight = Math.min(window.innerHeight * 0.7, 400);

  canvas.width = maxWidth;
  canvas.height = maxWidth * 2 / 3;

  if (canvas.height > maxHeight) {
    canvas.height = maxHeight;
    canvas.width = maxHeight * 3 / 2;
  }

  scale = canvas.width / 600;

  // Justera positioner och storlekar
  leftPaddle.x = 0;
  leftPaddle.height = 80 * scale;
  leftPaddle.speed = 5 * scale;
  rightPaddle.x = canvas.width - PADDLE_WIDTH * scale;
  rightPaddle.height = 60 * scale;
  rightPaddle.speed = 4 * scale;
  ball.size = 8 * scale;
  ball.dx = 4 * scale;
  ball.dy = 4 * scale;

  resetBall();
}

function startGame(computer) {
  vsComputer = computer;
  startScreen.style.display = 'none';
  gameState = 'playing';
  leftScore = 0;
  rightScore = 0;
  updateScore();
  status.textContent = 'Spelet är igång! Använd W/S, pilar eller dra på skärmen.';
  resetBall();

  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId);
  }

  lastTime = performance.now();
  gameLoop(lastTime);
}

function resetBall() {
  ball.x = canvas.width / 2;
  ball.y = canvas.height / 2;
  ball.dx = (Math.random() > 0.5 ? 1 : -1) * 4 * scale;
  ball.dy = (Math.random() - 0.5) * 4 * scale;
  leftPaddle.y = canvas.height / 2 - leftPaddle.height / 2;
  rightPaddle.y = canvas.height / 2 - rightPaddle.height / 2;
}

function updateScore() {
  scoreDisplay.textContent = leftScore + ' - ' + rightScore;
}

function gameLoop(timestamp) {
  if (gameState === 'gameover') {
    drawGameOver();
    return;
  }

  if (gameState !== 'playing') {
    if (gameState === 'paused') {
      drawPaused();
    }
    animationFrameId = requestAnimationFrame(gameLoop);
    return;
  }

  if (timestamp - lastTime < frameDelay) {
    animationFrameId = requestAnimationFrame(gameLoop);
    return;
  }
  lastTime = timestamp;

  updateGame();
  drawGame();

  animationFrameId = requestAnimationFrame(gameLoop);
}

function updateGame() {
  // Flytta paddlar (tangentbord)
  if (keys.up && leftPaddle.y > 0) {
    leftPaddle.y -= leftPaddle.speed;
  }
  if (keys.down && leftPaddle.y < canvas.height - leftPaddle.height) {
    leftPaddle.y += leftPaddle.speed;
  }

  if (!vsComputer) {
    if (keys.i && rightPaddle.y > 0) {
      rightPaddle.y -= rightPaddle.speed;
    }
    if (keys.k && rightPaddle.y < canvas.height - rightPaddle.height) {
      rightPaddle.y += rightPaddle.speed;
    }
  } else {
    // Dator-AI (med 30% chans att missa)
    if (rightPaddle.y + rightPaddle.height / 2 < ball.y - 10 && Math.random() > 0.3) {
      rightPaddle.y += rightPaddle.speed * 0.8;
    } else if (rightPaddle.y + rightPaddle.height / 2 > ball.y + 10 && Math.random() > 0.3) {
      rightPaddle.y -= rightPaddle.speed * 0.8;
    }
  }

  // Flytta bollen
  ball.x += ball.dx;
  ball.y += ball.dy;

  // Kollision med tak och golv
  if (ball.y <= 0 || ball.y >= canvas.height - ball.size) {
    ball.dy *= -1;
  }

  // Kollision med paddlar
  if (
    ball.x <= leftPaddle.x + PADDLE_WIDTH * scale &&
    ball.y + ball.size >= leftPaddle.y &&
    ball.y <= leftPaddle.y + leftPaddle.height
  ) {
    ball.dx = Math.abs(ball.dx);
  }

  if (
    ball.x + ball.size >= rightPaddle.x &&
    ball.y + ball.size >= rightPaddle.y &&
    ball.y <= rightPaddle.y + rightPaddle.height
  ) {
    ball.dx = -Math.abs(ball.dx);
  }

  // Poäng
  if (ball.x < 0) {
    rightScore++;
    updateScore();
    resetBall();
  }
  if (ball.x > canvas.width) {
    leftScore++;
    updateScore();
    resetBall();
  }

  // Vinnare
  if (leftScore >= WINNING_SCORE || rightScore >= WINNING_SCORE) {
    gameState = 'gameover';
  }
}

function drawGame() {
  // Rensa canvas
  ctx.fillStyle = 'black';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Rita mittlinje
  ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
  ctx.lineWidth = 2 * scale;
  ctx.beginPath();
  ctx.moveTo(canvas.width / 2, 0);
  ctx.lineTo(canvas.width / 2, canvas.height);
  ctx.stroke();

  // Rita paddlar
  ctx.fillStyle = '#007BBF';
  ctx.fillRect(leftPaddle.x, leftPaddle.y, PADDLE_WIDTH * scale, leftPaddle.height);
  ctx.fillRect(rightPaddle.x, rightPaddle.y, PADDLE_WIDTH * scale, rightPaddle.height);

  // Rita boll
  ctx.fillStyle = 'white';
  ctx.fillRect(ball.x, ball.y, ball.size, ball.size);
}

function drawGameOver() {
  ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.fillStyle = 'white';
  ctx.font = Math.round(20 * scale) + 'px Arial';
  ctx.textAlign = 'center';

  if (leftScore >= WINNING_SCORE) {
    ctx.fillText('Tanten vann!', canvas.width / 2, canvas.height / 2 - 20 * scale);
  } else if (vsComputer) {
    ctx.fillText('Datorn vann!', canvas.width / 2, canvas.height / 2 - 20 * scale);
  } else {
    ctx.fillText('Spelare 2 vann!', canvas.width / 2, canvas.height / 2 - 20 * scale);
  }

  ctx.font = Math.round(16 * scale) + 'px Arial';
  ctx.fillText('Tryck på Starta för att spela igen', canvas.width / 2, canvas.height / 2 + 20 * scale);

  startScreen.style.display = 'block';
  startScreen.querySelector('h1').textContent = 'Spelet är slut!';
}

function drawPaused() {
  ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.fillStyle = 'white';
  ctx.font = Math.round(20 * scale) + 'px Arial';
  ctx.textAlign = 'center';
  ctx.fillText('Pausad', canvas.width / 2, canvas.height / 2);
}

// Starta spelet
initGame();

</script> </body> </html>