<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Single Player Pong</title>

  <style>

    body {

      display: flex;

      justify-content: center;

      align-items: center;

      margin: 0;

      background: black;

      height: 100vh;

      overflow: hidden;

    }

    canvas {

      border: 2px solid white;

    }

  </style>

</head>

<body>

  <canvas id="pong"></canvas>


  <script>

    const canvas = document.getElementById('pong');

    const ctx = canvas.getContext('2d');


    // Set canvas size to fit the screen

    canvas.width = window.innerWidth * 0.9;

    canvas.height = window.innerHeight * 0.8;


    const paddleWidth = 10;

    const paddleHeight = 100;

    const ballSize = 10;

    const maxScore = 21;


    const player = { x: 10, y: canvas.height / 2 - paddleHeight / 2, score: 0 };

    const computer = { x: canvas.width - 20, y: canvas.height / 2 - paddleHeight / 2, score: 0 };

    const ball = { x: canvas.width / 2, y: canvas.height / 2, dx: 4, dy: 4, speed: 4 };


    let touchY = player.y; // Track touch position for the player's paddle

    let gameOver = false;

    let countdown = 0;

    let rallyCount = 0; // Counts consecutive hits to increase speed


    const drawRect = (x, y, width, height, color) => {

      ctx.fillStyle = color;

      ctx.fillRect(x, y, width, height);

    };


    const drawCircle = (x, y, radius, color) => {

      ctx.fillStyle = color;

      ctx.beginPath();

      ctx.arc(x, y, radius, 0, Math.PI * 2);

      ctx.closePath();

      ctx.fill();

    };


    const drawText = (text, x, y, color, fontSize = "30px") => {

      ctx.fillStyle = color;

      ctx.font = `${fontSize} Arial`;

      ctx.fillText(text, x, y);

    };


    const resetBall = () => {

      ball.x = canvas.width / 2;

      ball.y = canvas.height / 2;


      // Ensure the ball launches with a random angle between 0 and 45 degrees

      const angle = (Math.random() * Math.PI) / 4; // Random angle between 0 and 45 degrees

      ball.speed = 4; // Reset speed to initial value

      rallyCount = 0; // Reset rally count

      ball.dx = ball.speed * (Math.random() > 0.5 ? 1 : -1);

      ball.dy = ball.speed * Math.tan(angle) * (Math.random() > 0.5 ? 1 : -1);

    };


    const startCountdown = (callback) => {

      countdown = 3;

      const interval = setInterval(() => {

        countdown--;

        if (countdown < 0) {

          clearInterval(interval);

          callback(); // Resume the game

        }

      }, 1000);

    };


    const checkWinCondition = () => {

      if (player.score >= maxScore || computer.score >= maxScore) {

        gameOver = true;

      }

    };


    const update = () => {

      if (gameOver) return;


      // Allow paddle movement even during countdown

      player.y += (touchY - player.y) * 0.2;


      // Prevent paddles from going out of bounds

      player.y = Math.max(Math.min(player.y, canvas.height - paddleHeight), 0);

      computer.y = Math.max(Math.min(computer.y, canvas.height - paddleHeight), 0);


      if (countdown > 0) return; // Pause ball and AI during countdown


      // Ball movement

      ball.x += ball.dx;

      ball.y += ball.dy;


      // Bounce off top and bottom walls

      if (ball.y <= 0 || ball.y + ballSize >= canvas.height) {

        ball.dy *= -1;

      }


      // Bounce off paddles

      if (

        ball.x <= player.x + paddleWidth &&

        ball.y >= player.y &&

        ball.y <= player.y + paddleHeight

      ) {

        ball.dx = Math.abs(ball.dx); // Ensure the ball moves to the right

        rallyCount++;

        ball.speed += 0.5; // Increase speed slightly after each rally

        ball.dx = Math.sign(ball.dx) * ball.speed;

        ball.dy = Math.sign(ball.dy) * ball.speed;

      }


      if (

        ball.x + ballSize >= computer.x &&

        ball.y >= computer.y &&

        ball.y <= computer.y + paddleHeight

      ) {

        ball.dx = -Math.abs(ball.dx); // Ensure the ball moves to the left

        rallyCount++;

        ball.speed += 0.5; // Increase speed slightly after each rally

        ball.dx = Math.sign(ball.dx) * ball.speed;

        ball.dy = Math.sign(ball.dy) * ball.speed;

      }


      // Scoring

      if (ball.x < 0) {

        computer.score++;

        resetBall();

        startCountdown(() => {});

      }

      if (ball.x > canvas.width) {

        player.score++;

        resetBall();

        startCountdown(() => {});

      }


      // Prevent the ball from getting stuck behind the paddle

      if (ball.x < 0 && ball.dx < 0) {

        resetBall();

        computer.score++;

        startCountdown(() => {});

      }


      // AI speed and behavior

      const computerSpeed = 3;

      if (computer.y + paddleHeight / 2 < ball.y) {

        computer.y += computerSpeed;

      } else {

        computer.y -= computerSpeed;

      }


      checkWinCondition();

    };


    const render = () => {

      drawRect(0, 0, canvas.width, canvas.height, "black");

      drawRect(player.x, player.y, paddleWidth, paddleHeight, "white");

      drawRect(computer.x, computer.y, paddleWidth, paddleHeight, "white");

      drawCircle(ball.x, ball.y, ballSize, "white");

      drawText(player.score, canvas.width / 4, 50, "white");

      drawText(computer.score, (canvas.width * 3) / 4, 50, "white");


      if (gameOver) {

        const winner = player.score >= maxScore ? "You Win!" : "AI Wins!";

        drawText(winner, canvas.width / 2 - 100, canvas.height / 2, "red");

        drawText("Reload to Play Again", canvas.width / 2 - 150, canvas.height / 2 + 50, "white");

      } else if (countdown > 0) {

        drawText(countdown, canvas.width / 2 - 15, canvas.height / 2, "white", "50px");

      }

    };


    const gameLoop = () => {

      update();

      render();

      if (!gameOver) requestAnimationFrame(gameLoop);

    };


    // Handle touch input

    canvas.addEventListener("touchmove", (event) => {

      const touch = event.touches[0];

      const rect = canvas.getBoundingClientRect();

      touchY = touch.clientY - rect.top - paddleHeight / 2;

    });


    // Start the initial countdown

    startCountdown(() => {

      gameLoop();

    });

  </script>

</body>

</html>

Comments