Webbserver - Love Blomberg

Show sourcecode

The following files exists in this folder. Click to view.

public_html/gamla-kurser/webbutv2/projekt/snake/js/

snake.js
snake.sync-conflict-20250925-184038-H7ZHIM2.js

snake.js

459 lines UTF-8 Windows (CRLF)
// Grundläggande spelvariabler
const blockSize = 25;
const rows = 18;
const cols = 18;
const board = document.getElementById("board");
const ctx = board.getContext("2d");
const scoreBoard = document.getElementById("scoreBoard");

// Orm och mat
let snake = {
  x: blockSize * 5,
  y: blockSize * 5,
  dx: 0,
  dy: 0,
  body: [],
  score: 0,
  color: "#3498db"
};
let food = { x: 0, y: 0 };
let blocks = [];

// Speltillstånd
let gameLoop;
let isPaused = true;
let gameOver = false;
let directionChanged = false;
let highScore = localStorage.getItem("snakeHighScore") || 0;

// Spelkonfiguration
const gameOptions = {
  enableWalls: true,
  gameSpeed: 10,
  speedIncreaseThreshold: 5, // Öka hastighet var 5:e poäng
  enableWrapAround: false
};

// Färger
const colors = {
  background: "#1e1e1e",
  grid: "#2a2a2a",
  food: "#e74c3c",
  wall: "#95a5a6",
  snake: "#3498db"
};

// Initialisera spelet
window.onload = function() {
  // Sätt spelplanens storlek
  board.height = rows * blockSize;
  board.width = cols * blockSize;
  
  // Lägg till händelselyssnare
  document.addEventListener("keydown", handleKeyPress);
  document.querySelectorAll(".game-option").forEach(el => 
    el.addEventListener("change", updateSettings)
  );
  
  // Starta spelet
  restart();
};

// Rita spelplanen
function draw() {
  // Rita bakgrund
  ctx.fillStyle = colors.background;
  ctx.fillRect(0, 0, board.width, board.height);
  
  // Rita rutnät
  ctx.fillStyle = colors.grid;
  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {
      if ((row + col) % 2 === 1) {
        ctx.fillRect(col * blockSize, row * blockSize, blockSize, blockSize);
      }
    }
  }
  
  // Rita mat
  ctx.fillStyle = colors.food;
  ctx.fillRect(food.x, food.y, blockSize, blockSize);
  
  // Rita väggar om aktiverade
  if (gameOptions.enableWalls) {
    ctx.fillStyle = colors.wall;
    blocks.forEach(block => {
      ctx.fillRect(block[0], block[1], blockSize, blockSize);
    });
  }
  
  // Rita ormkropp
  for (let i = 0; i < snake.body.length; i++) {
    ctx.fillStyle = colors.snake;
    ctx.fillRect(snake.body[i][0], snake.body[i][1], blockSize, blockSize);
    ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
    ctx.strokeRect(snake.body[i][0], snake.body[i][1], blockSize, blockSize);
  }
  
  // Rita ormhuvud
  ctx.fillStyle = colors.snake;
  ctx.fillRect(snake.x, snake.y, blockSize, blockSize);
}

// Hantera tangentkontroller
function handleKeyPress(e) {
  // Förhindra sidorullning
  if (["Space", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", 
       "KeyW", "KeyS", "KeyA", "KeyD"].includes(e.code)) {
    e.preventDefault();
  }
  
  // Hantera mellanslag för paus/fortsätt
  if (e.code === "Space") {
    togglePause();
    return;
  }
  
  if (!isPaused) {
    // Ormkontroller (pilar eller WASD)
    if ((e.code === "ArrowUp" || e.code === "KeyW") && snake.dy !== 1) {
      if (!directionChanged) {
        snake.dx = 0;
        snake.dy = -1;
        directionChanged = true;
      }
    } else if ((e.code === "ArrowDown" || e.code === "KeyS") && snake.dy !== -1) {
      if (!directionChanged) {
        snake.dx = 0;
        snake.dy = 1;
        directionChanged = true;
      }
    } else if ((e.code === "ArrowLeft" || e.code === "KeyA") && snake.dx !== 1) {
      if (!directionChanged) {
        snake.dx = -1;
        snake.dy = 0;
        directionChanged = true;
      }
    } else if ((e.code === "ArrowRight" || e.code === "KeyD") && snake.dx !== -1) {
      if (!directionChanged) {
        snake.dx = 1;
        snake.dy = 0;
        directionChanged = true;
      }
    }
  }
}

// Växla spelpausstatus
function togglePause() {
  const overlay = document.getElementById("gameOverlay");
  
  if (gameOver) {
    restart();
    gameOver = false;
    isPaused = false;
    overlay.style.display = "none";
    gameLoop = setInterval(update, 1000 / getCurrentSpeed());
    return;
  }
  
  if (isPaused) {
    isPaused = false;
    overlay.style.display = "none";
    gameLoop = setInterval(update, 1000 / getCurrentSpeed());
  } else {
    isPaused = true;
    document.getElementById("overlayMessage").textContent = "PAUSAD";
    document.getElementById("overlaySubMessage").textContent = "Tryck på mellanslag för att fortsätta";
    overlay.style.display = "flex";
    clearInterval(gameLoop);
  }
}

// Beräkna aktuell spelhastighet baserat på poäng
function getCurrentSpeed() {
  // Hastigheten ökar med 1 var 5e poäng
  const speed = gameOptions.gameSpeed + Math.floor(snake.score / gameOptions.speedIncreaseThreshold);
  return speed;
}

// Huvuduppdateringsfunktion för spelet
function update() {
  if (gameOver) {
    restart();
    return;
  }
  
  directionChanged = false;
  
  // Flytta ormens kroppsdelar
  if (snake.body.length) {
    for (let i = snake.body.length - 1; i > 0; i--) {
      snake.body[i] = snake.body[i - 1];
    }
    snake.body[0] = [snake.x, snake.y];
  }
  
  // Flytta ormhuvudet
  snake.x += snake.dx * blockSize;
  snake.y += snake.dy * blockSize;
  
  // Kontrollera väggkollision
  if (
    snake.x < 0 || 
    snake.x >= cols * blockSize || 
    snake.y < 0 || 
    snake.y >= rows * blockSize
  ) {
    if (gameOptions.enableWrapAround) {
      // Wrap around logik
      if (snake.x < 0) {
        snake.x = (cols - 1) * blockSize;
      } else if (snake.x >= cols * blockSize) {
        snake.x = 0;
      }
      
      if (snake.y < 0) {
        snake.y = (rows - 1) * blockSize;
      } else if (snake.y >= rows * blockSize) {
        snake.y = 0;
      }
    } else {
      // Vanlig kollisionslogik
      endGame();
      return;
    }
  }
  
  // Kontrollera kollision med block
  for (let i = 0; i < blocks.length; i++) {
    if (snake.x === blocks[i][0] && snake.y === blocks[i][1]) {
      endGame();
      return;
    }
  }
  
  // Kontrollera kollision med ormkropp (men skippa första segmentet om vi precis ätit)
  let startIndex = snake.justAte ? 1 : 0;
  for (let i = startIndex; i < snake.body.length; i++) {
    if (snake.x === snake.body[i][0] && snake.y === snake.body[i][1]) {
      endGame();
      return;
    }
  }
  
  // Kontrollera matkollision
  if (snake.x === food.x && snake.y === food.y) {
    // Markera att vi precis ätit
    snake.justAte = true;
    
    // Lägg till ny kroppsdel (använd sista positionen i kropppen eller huvudet)
    if (snake.body.length > 0) {
      snake.body.push([...snake.body[snake.body.length - 1]]);
    } else {
      snake.body.push([snake.x - snake.dx * blockSize, snake.y - snake.dy * blockSize]);
    }
    
    // Öka poäng
    snake.score++;
    
    // Uppdatera högsta poäng
    if (snake.score > highScore) {
      highScore = snake.score;
      localStorage.setItem("snakeHighScore", highScore);
    }
    
    // Placera ny mat
    placeFood();
    
    // Uppdatera spelhastighet om poängen når tröskelvärdet
    if (snake.score % gameOptions.speedIncreaseThreshold === 0) {
      clearInterval(gameLoop);
      gameLoop = setInterval(update, 1000 / getCurrentSpeed());
    }
  } else {
    // Om vi inte ätit mat denna uppdatering, återställ justAte-flaggan
    snake.justAte = false;
  }
  
  // Uppdatera poängtavla och rita allt
  updateScoreboard();
  draw();
}

// Avsluta spelet
function endGame() {
  clearInterval(gameLoop);
  gameOver = true;
  const overlay = document.getElementById("gameOverlay");
  document.getElementById("overlayMessage").textContent = "SPELET SLUT";
  document.getElementById("overlaySubMessage").textContent = "Tryck på mellanslag för att spela igen";
  overlay.style.display = "flex";
}

// Starta om spelet
function restart() {
  clearInterval(gameLoop);
  
  // Återställ ormen
  snake = {
    x: blockSize * 5,
    y: blockSize * 5,
    dx: 0,
    dy: 0,
    body: [],
    score: 0,
    color: "#3498db",
    justAte: false
  };
  
  // Återställ block
  blocks = [];
  
  // Lägg till väggar om aktiverade
  if (gameOptions.enableWalls) {
    for (let i = 0; i < 10; i++) {
      spawnBlock();
    }
  }
  
  // Placera ny mat
  placeFood();
  updateScoreboard();
  draw();
  
  // Visa startmeddelande
  isPaused = true;
  document.getElementById("overlayMessage").textContent = "Tryck på mellanslag för att starta";
  document.getElementById("overlaySubMessage").textContent = "";
  document.getElementById("gameOverlay").style.display = "flex";
}

// Placera mat slumpmässigt
function placeFood() {
  let isValid;
  
  // Hitta en giltig position
  do {
    isValid = true;
    food = {
      x: Math.floor(Math.random() * cols) * blockSize,
      y: Math.floor(Math.random() * rows) * blockSize
    };
    
    // Kontrollera kollision med ormhuvud
    if (food.x === snake.x && food.y === snake.y) {
      isValid = false;
      continue;
    }
    
    // Kontrollera kollision med ormkropp
    for (let i = 0; i < snake.body.length; i++) {
      if (food.x === snake.body[i][0] && food.y === snake.body[i][1]) {
        isValid = false;
        break;
      }
    }
    
    // Kontrollera kollision med block
    for (let i = 0; i < blocks.length; i++) {
      if (food.x === blocks[i][0] && food.y === blocks[i][1]) {
        isValid = false;
        break;
      }
    }
  } while (!isValid);
}

// Skapa blockhinder
function spawnBlock() {
  let x, y;
  let isValid;
  
  // Hitta en giltig position
  do {
    isValid = true;
    x = Math.floor(Math.random() * cols) * blockSize;
    y = Math.floor(Math.random() * rows) * blockSize;
    
    // Kontrollera kollision med orm
    if (x === snake.x && y === snake.y) {
      isValid = false;
      continue;
    }
    
    // Kontrollera kollision med ormkropp
    for (let i = 0; i < snake.body.length; i++) {
      if (x === snake.body[i][0] && y === snake.body[i][1]) {
        isValid = false;
        break;
      }
    }
    
    // Kontrollera kollision med mat
    if (x === food.x && y === food.y) {
      isValid = false;
      continue;
    }
    
    // Kontrollera kollision med andra block
    for (let i = 0; i < blocks.length; i++) {
      if (x === blocks[i][0] && y === blocks[i][1]) {
        isValid = false;
        break;
      }
    }
  } while (!isValid);
  
  blocks.push([x, y]);
}

// Uppdatera poängtavla
function updateScoreboard() {
  scoreBoard.innerHTML = `
    <p>Högsta poäng: ${highScore}</p>
    <p>Poäng: ${snake.score}</p>
  `;
}

// Uppdatera spelinställningar
function updateSettings() {
  const wallsToggle = document.getElementById("toggleWalls");
  const speedSlider = document.getElementById("gameSpeed");
  const wrapToggle = document.getElementById("toggleWrap");
  
  // Uppdatera vägginställning
  if (wallsToggle) {
    gameOptions.enableWalls = wallsToggle.checked;
    
    if (!gameOptions.enableWalls) {
      blocks = [];
    } else if (blocks.length === 0) {
      // Lägg till väggar om aktiverade
      if (gameOptions.enableWalls) {
        for (let i = 0; i < 10; i++) {
          spawnBlock();
        }
      }
    }
  }
  
  // Uppdatera wrap around-inställning
  if (wrapToggle) {
    gameOptions.enableWrapAround = wrapToggle.checked;
  }
  
  // Uppdatera hastighetsinställning
  if (speedSlider) {
    gameOptions.gameSpeed = parseInt(speedSlider.value);
    
    if (!isPaused && !gameOver) {
      clearInterval(gameLoop);
      gameLoop = setInterval(update, 1000 / getCurrentSpeed());
    }
  }
  
  updateScoreboard();
  draw();
}