This commit is contained in:
sotos
2025-12-10 13:56:56 +01:00
parent 9fab4a6a9d
commit 4fb9e846b2
4 changed files with 133 additions and 189 deletions

3
Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM nginx:alpine
COPY . /usr/share/nginx/html
EXPOSE 80

View File

@@ -1,33 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=VT323&display=swap"
rel="stylesheet"
/>
<title>Snake Game</title>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div>
<div class="scores">
<h1 id="score">000</h1>
<h1 id="highScore">000</h1>
</div>
<div class="game-border-1">
<div class="game-border-2">
<div class="game-border-3">
<div id="game-board"></div>
</div>
</div>
</div>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
<title>Snake Game</title>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div class="container">
<div class="scores">
<h1 id="score">000</h1>
<h1 id="highScore">000</h1>
</div>
<h1 id="instruction-text">Press spacebar to start the game</h1>
<div id="game-board"></div>
<h1 id="instruction-text">Press spacebar or tap to start the game</h1>
<img id="logo" src="snake-game-ai-gen.png" alt="snake-logo" />
</body>
</div>
</body>
</html>

207
script.js
View File

@@ -1,11 +1,11 @@
// Define HTML elements
// HTML elements
const board = document.getElementById('game-board');
const instructionText = document.getElementById('instruction-text');
const logo = document.getElementById('logo');
const score = document.getElementById('score');
const highScoreText = document.getElementById('highScore');
// Define game variables
// Game variables
const gridSize = 20;
let snake = [{ x: 10, y: 10 }];
let food = generateFood();
@@ -15,7 +15,14 @@ let gameInterval;
let gameSpeedDelay = 200;
let gameStarted = false;
// Draw game map, snake, food
// Swipe variables
let touchStartX = 0;
let touchStartY = 0;
let touchEndX = 0;
let touchEndY = 0;
const minSwipeDistance = 30;
// Draw game
function draw() {
board.innerHTML = '';
drawSnake();
@@ -25,153 +32,119 @@ function draw() {
// Draw snake
function drawSnake() {
snake.forEach((segment) => {
const snakeElement = createGameElement('div', 'snake');
setPosition(snakeElement, segment);
snake.forEach(segment => {
const snakeElement = document.createElement('div');
snakeElement.className = 'snake';
snakeElement.style.gridColumnStart = segment.x;
snakeElement.style.gridRowStart = segment.y;
board.appendChild(snakeElement);
});
}
// Create a snake or food cube/div
function createGameElement(tag, className) {
const element = document.createElement(tag);
element.className = className;
return element;
}
// Set the position of snake or food
function setPosition(element, position) {
element.style.gridColumn = position.x;
element.style.gridRow = position.y;
}
// Testing draw function
// draw();
// Draw food function
// Draw food
function drawFood() {
if (gameStarted) {
const foodElement = createGameElement('div', 'food');
setPosition(foodElement, food);
board.appendChild(foodElement);
}
if (!gameStarted) return;
const foodElement = document.createElement('div');
foodElement.className = 'food';
foodElement.style.gridColumnStart = food.x;
foodElement.style.gridRowStart = food.y;
board.appendChild(foodElement);
}
// Generate food
function generateFood() {
const x = Math.floor(Math.random() * gridSize) + 1;
const y = Math.floor(Math.random() * gridSize) + 1;
return { x, y };
return {
x: Math.floor(Math.random() * gridSize) + 1,
y: Math.floor(Math.random() * gridSize) + 1
};
}
// Moving the snake
// Move snake
function move() {
const head = { ...snake[0] };
switch (direction) {
case 'up':
head.y--;
break;
case 'down':
head.y++;
break;
case 'left':
head.x--;
break;
case 'right':
head.x++;
break;
case 'up': head.y--; break;
case 'down': head.y++; break;
case 'left': head.x--; break;
case 'right': head.x++; break;
}
snake.unshift(head);
// snake.pop();
if (head.x === food.x && head.y === food.y) {
food = generateFood();
increaseSpeed();
clearInterval(gameInterval); // Clear past interval
gameInterval = setInterval(() => {
move();
checkCollision();
draw();
}, gameSpeedDelay);
} else {
snake.pop();
}
resetInterval();
} else snake.pop();
}
// Test moving
// setInterval(() => {
// move(); // Move first
// draw(); // Then draw again new position
// }, 200);
// Start game function
// Start game
function startGame() {
gameStarted = true; // Keep track of a running game
if (gameStarted) return;
gameStarted = true;
instructionText.style.display = 'none';
logo.style.display = 'none';
gameInterval = setInterval(() => {
move();
checkCollision();
draw();
}, gameSpeedDelay);
gameInterval = setInterval(() => { move(); checkCollision(); draw(); }, gameSpeedDelay);
}
// Keypress event listener
function handleKeyPress(event) {
if (
(!gameStarted && event.code === 'Space') ||
(!gameStarted && event.key === ' ')
) {
startGame();
} else {
switch (event.key) {
case 'ArrowUp':
direction = 'up';
break;
case 'ArrowDown':
direction = 'down';
break;
case 'ArrowLeft':
direction = 'left';
break;
case 'ArrowRight':
direction = 'right';
break;
}
}
// Keyboard control
function handleKeyPress(e) {
if (!gameStarted && (e.code === 'Space' || e.key === ' ')) startGame();
if (e.key === 'ArrowUp' && direction !== 'down') direction = 'up';
if (e.key === 'ArrowDown' && direction !== 'up') direction = 'down';
if (e.key === 'ArrowLeft' && direction !== 'right') direction = 'left';
if (e.key === 'ArrowRight' && direction !== 'left') direction = 'right';
}
document.addEventListener('keydown', handleKeyPress);
function increaseSpeed() {
// console.log(gameSpeedDelay);
if (gameSpeedDelay > 150) {
gameSpeedDelay -= 5;
} else if (gameSpeedDelay > 100) {
gameSpeedDelay -= 3;
} else if (gameSpeedDelay > 50) {
gameSpeedDelay -= 2;
} else if (gameSpeedDelay > 25) {
gameSpeedDelay -= 1;
// Swipe control
board.addEventListener('touchstart', e => {
const touch = e.touches[0];
touchStartX = touch.clientX;
touchStartY = touch.clientY;
});
board.addEventListener('touchend', e => {
const touch = e.changedTouches[0];
touchEndX = touch.clientX;
touchEndY = touch.clientY;
handleSwipe();
});
board.addEventListener('touchmove', e => e.preventDefault(), { passive: false });
function handleSwipe() {
const dx = touchEndX - touchStartX;
const dy = touchEndY - touchStartY;
if (Math.abs(dx) < minSwipeDistance && Math.abs(dy) < minSwipeDistance) return;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 0 && direction !== 'left') direction = 'right';
else if (dx < 0 && direction !== 'right') direction = 'left';
} else {
if (dy > 0 && direction !== 'up') direction = 'down';
else if (dy < 0 && direction !== 'down') direction = 'up';
}
if (!gameStarted) startGame();
}
// Increase speed
function increaseSpeed() {
if (gameSpeedDelay > 150) gameSpeedDelay -= 5;
else if (gameSpeedDelay > 100) gameSpeedDelay -= 3;
else if (gameSpeedDelay > 50) gameSpeedDelay -= 2;
else if (gameSpeedDelay > 25) gameSpeedDelay -= 1;
}
function resetInterval() {
clearInterval(gameInterval);
gameInterval = setInterval(() => { move(); checkCollision(); draw(); }, gameSpeedDelay);
}
// Collision
function checkCollision() {
const head = snake[0];
if (head.x < 1 || head.x > gridSize || head.y < 1 || head.y > gridSize) {
resetGame();
}
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
resetGame();
}
}
if (head.x < 1 || head.x > gridSize || head.y < 1 || head.y > gridSize) resetGame();
for (let i = 1; i < snake.length; i++) if (head.x === snake[i].x && head.y === snake[i].y) resetGame();
}
// Reset game
function resetGame() {
updateHighScore();
stopGame();
@@ -179,21 +152,19 @@ function resetGame() {
food = generateFood();
direction = 'right';
gameSpeedDelay = 200;
updateScore();
draw();
}
// Score
function updateScore() {
const currentScore = snake.length - 1;
score.textContent = currentScore.toString().padStart(3, '0');
score.textContent = (snake.length - 1).toString().padStart(3, '0');
}
function stopGame() {
clearInterval(gameInterval);
gameStarted = false;
instructionText.style.display = 'block';
logo.style.display = 'block';
}
function updateHighScore() {
const currentScore = snake.length - 1;
if (currentScore > highScore) {

View File

@@ -5,53 +5,31 @@ body {
height: 100vh;
margin: 0;
font-family: 'VT323', monospace;
}
body,
.snake {
background-color: #414141;
}
#game-board {
border-radius: 100px;
display: grid;
grid-template-columns: repeat(20, 20px);
grid-template-rows: repeat(20, 20px);
margin: 5px;
}
.game-border-1 {
border: #595f43 solid 10px;
border-radius: 30px;
box-shadow: inset 0 0 0 10px #595f43;
}
.game-border-2 {
border: #abb78a solid 8px;
border-radius: 26px;
box-shadow: inset 0 0 0 10px #abb78a;
}
.game-border-3 {
border: #8b966c solid 30px;
border-radius: 20px;
box-shadow: inset 0 0 0 5px #8b966c;
}
#instruction-text {
position: absolute;
top: 60%;
color: #333;
width: 300px;
.container {
position: relative;
text-align: center;
text-transform: capitalize;
padding: 30px;
margin: 0;
}
#game-board {
width: 400px;
height: 400px;
display: grid;
grid-template-columns: repeat(20, 1fr);
grid-template-rows: repeat(20, 1fr);
background-color: #c4cfa3;
margin: 20px auto;
border-radius: 20px;
touch-action: none; /* verhindert Scrollen bei Swipe */
}
.scores {
display: flex;
justify-content: space-between;
width: 400px;
margin: auto;
}
#score {
@@ -70,20 +48,21 @@ body,
display: none;
}
.game-border-3,
#logo {
background-color: #c4cfa3;
#instruction-text {
color: #333;
margin-top: 10px;
}
.snake {
background-color: #414141;
border: #5a5a5a 1px dotted;
}
.food {
background-color: #dedede;
border: #999 5px solid;
border: #999 2px solid;
}
#logo {
position: absolute;
margin-top: 10px;
}