Snake
This commit is contained in:
3
Dockerfile
Normal file
3
Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
COPY . /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
17
index.html
17
index.html
@@ -5,29 +5,20 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<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.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
|
||||||
href="https://fonts.googleapis.com/css2?family=VT323&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<title>Snake Game</title>
|
<title>Snake Game</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script src="script.js" defer></script>
|
<script src="script.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div class="container">
|
||||||
<div class="scores">
|
<div class="scores">
|
||||||
<h1 id="score">000</h1>
|
<h1 id="score">000</h1>
|
||||||
<h1 id="highScore">000</h1>
|
<h1 id="highScore">000</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-border-1">
|
|
||||||
<div class="game-border-2">
|
|
||||||
<div class="game-border-3">
|
|
||||||
<div id="game-board"></div>
|
<div id="game-board"></div>
|
||||||
</div>
|
<h1 id="instruction-text">Press spacebar or tap to start the game</h1>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h1 id="instruction-text">Press spacebar to start the game</h1>
|
|
||||||
<img id="logo" src="snake-game-ai-gen.png" alt="snake-logo" />
|
<img id="logo" src="snake-game-ai-gen.png" alt="snake-logo" />
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
205
script.js
205
script.js
@@ -1,11 +1,11 @@
|
|||||||
// Define HTML elements
|
// HTML elements
|
||||||
const board = document.getElementById('game-board');
|
const board = document.getElementById('game-board');
|
||||||
const instructionText = document.getElementById('instruction-text');
|
const instructionText = document.getElementById('instruction-text');
|
||||||
const logo = document.getElementById('logo');
|
const logo = document.getElementById('logo');
|
||||||
const score = document.getElementById('score');
|
const score = document.getElementById('score');
|
||||||
const highScoreText = document.getElementById('highScore');
|
const highScoreText = document.getElementById('highScore');
|
||||||
|
|
||||||
// Define game variables
|
// Game variables
|
||||||
const gridSize = 20;
|
const gridSize = 20;
|
||||||
let snake = [{ x: 10, y: 10 }];
|
let snake = [{ x: 10, y: 10 }];
|
||||||
let food = generateFood();
|
let food = generateFood();
|
||||||
@@ -15,7 +15,14 @@ let gameInterval;
|
|||||||
let gameSpeedDelay = 200;
|
let gameSpeedDelay = 200;
|
||||||
let gameStarted = false;
|
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() {
|
function draw() {
|
||||||
board.innerHTML = '';
|
board.innerHTML = '';
|
||||||
drawSnake();
|
drawSnake();
|
||||||
@@ -25,153 +32,119 @@ function draw() {
|
|||||||
|
|
||||||
// Draw snake
|
// Draw snake
|
||||||
function drawSnake() {
|
function drawSnake() {
|
||||||
snake.forEach((segment) => {
|
snake.forEach(segment => {
|
||||||
const snakeElement = createGameElement('div', 'snake');
|
const snakeElement = document.createElement('div');
|
||||||
setPosition(snakeElement, segment);
|
snakeElement.className = 'snake';
|
||||||
|
snakeElement.style.gridColumnStart = segment.x;
|
||||||
|
snakeElement.style.gridRowStart = segment.y;
|
||||||
board.appendChild(snakeElement);
|
board.appendChild(snakeElement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a snake or food cube/div
|
// Draw food
|
||||||
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
|
|
||||||
function drawFood() {
|
function drawFood() {
|
||||||
if (gameStarted) {
|
if (!gameStarted) return;
|
||||||
const foodElement = createGameElement('div', 'food');
|
const foodElement = document.createElement('div');
|
||||||
setPosition(foodElement, food);
|
foodElement.className = 'food';
|
||||||
|
foodElement.style.gridColumnStart = food.x;
|
||||||
|
foodElement.style.gridRowStart = food.y;
|
||||||
board.appendChild(foodElement);
|
board.appendChild(foodElement);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Generate food
|
// Generate food
|
||||||
function generateFood() {
|
function generateFood() {
|
||||||
const x = Math.floor(Math.random() * gridSize) + 1;
|
return {
|
||||||
const y = Math.floor(Math.random() * gridSize) + 1;
|
x: Math.floor(Math.random() * gridSize) + 1,
|
||||||
return { x, y };
|
y: Math.floor(Math.random() * gridSize) + 1
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moving the snake
|
// Move snake
|
||||||
function move() {
|
function move() {
|
||||||
const head = { ...snake[0] };
|
const head = { ...snake[0] };
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'up':
|
case 'up': head.y--; break;
|
||||||
head.y--;
|
case 'down': head.y++; break;
|
||||||
break;
|
case 'left': head.x--; break;
|
||||||
case 'down':
|
case 'right': head.x++; break;
|
||||||
head.y++;
|
|
||||||
break;
|
|
||||||
case 'left':
|
|
||||||
head.x--;
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
head.x++;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snake.unshift(head);
|
snake.unshift(head);
|
||||||
|
|
||||||
// snake.pop();
|
|
||||||
|
|
||||||
if (head.x === food.x && head.y === food.y) {
|
if (head.x === food.x && head.y === food.y) {
|
||||||
food = generateFood();
|
food = generateFood();
|
||||||
increaseSpeed();
|
increaseSpeed();
|
||||||
clearInterval(gameInterval); // Clear past interval
|
resetInterval();
|
||||||
gameInterval = setInterval(() => {
|
} else snake.pop();
|
||||||
move();
|
|
||||||
checkCollision();
|
|
||||||
draw();
|
|
||||||
}, gameSpeedDelay);
|
|
||||||
} else {
|
|
||||||
snake.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test moving
|
// Start game
|
||||||
// setInterval(() => {
|
|
||||||
// move(); // Move first
|
|
||||||
// draw(); // Then draw again new position
|
|
||||||
// }, 200);
|
|
||||||
|
|
||||||
// Start game function
|
|
||||||
function startGame() {
|
function startGame() {
|
||||||
gameStarted = true; // Keep track of a running game
|
if (gameStarted) return;
|
||||||
|
gameStarted = true;
|
||||||
instructionText.style.display = 'none';
|
instructionText.style.display = 'none';
|
||||||
logo.style.display = 'none';
|
logo.style.display = 'none';
|
||||||
gameInterval = setInterval(() => {
|
gameInterval = setInterval(() => { move(); checkCollision(); draw(); }, gameSpeedDelay);
|
||||||
move();
|
|
||||||
checkCollision();
|
|
||||||
draw();
|
|
||||||
}, gameSpeedDelay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keypress event listener
|
// Keyboard control
|
||||||
function handleKeyPress(event) {
|
function handleKeyPress(e) {
|
||||||
if (
|
if (!gameStarted && (e.code === 'Space' || e.key === ' ')) startGame();
|
||||||
(!gameStarted && event.code === 'Space') ||
|
if (e.key === 'ArrowUp' && direction !== 'down') direction = 'up';
|
||||||
(!gameStarted && event.key === ' ')
|
if (e.key === 'ArrowDown' && direction !== 'up') direction = 'down';
|
||||||
) {
|
if (e.key === 'ArrowLeft' && direction !== 'right') direction = 'left';
|
||||||
startGame();
|
if (e.key === 'ArrowRight' && direction !== 'left') direction = 'right';
|
||||||
} else {
|
|
||||||
switch (event.key) {
|
|
||||||
case 'ArrowUp':
|
|
||||||
direction = 'up';
|
|
||||||
break;
|
|
||||||
case 'ArrowDown':
|
|
||||||
direction = 'down';
|
|
||||||
break;
|
|
||||||
case 'ArrowLeft':
|
|
||||||
direction = 'left';
|
|
||||||
break;
|
|
||||||
case 'ArrowRight':
|
|
||||||
direction = 'right';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyPress);
|
document.addEventListener('keydown', handleKeyPress);
|
||||||
|
|
||||||
function increaseSpeed() {
|
// Swipe control
|
||||||
// console.log(gameSpeedDelay);
|
board.addEventListener('touchstart', e => {
|
||||||
if (gameSpeedDelay > 150) {
|
const touch = e.touches[0];
|
||||||
gameSpeedDelay -= 5;
|
touchStartX = touch.clientX;
|
||||||
} else if (gameSpeedDelay > 100) {
|
touchStartY = touch.clientY;
|
||||||
gameSpeedDelay -= 3;
|
});
|
||||||
} else if (gameSpeedDelay > 50) {
|
board.addEventListener('touchend', e => {
|
||||||
gameSpeedDelay -= 2;
|
const touch = e.changedTouches[0];
|
||||||
} else if (gameSpeedDelay > 25) {
|
touchEndX = touch.clientX;
|
||||||
gameSpeedDelay -= 1;
|
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() {
|
function checkCollision() {
|
||||||
const head = snake[0];
|
const head = snake[0];
|
||||||
|
if (head.x < 1 || head.x > gridSize || head.y < 1 || head.y > gridSize) resetGame();
|
||||||
if (head.x < 1 || head.x > gridSize || head.y < 1 || head.y > gridSize) {
|
for (let i = 1; i < snake.length; i++) if (head.x === snake[i].x && head.y === snake[i].y) resetGame();
|
||||||
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() {
|
function resetGame() {
|
||||||
updateHighScore();
|
updateHighScore();
|
||||||
stopGame();
|
stopGame();
|
||||||
@@ -179,21 +152,19 @@ function resetGame() {
|
|||||||
food = generateFood();
|
food = generateFood();
|
||||||
direction = 'right';
|
direction = 'right';
|
||||||
gameSpeedDelay = 200;
|
gameSpeedDelay = 200;
|
||||||
updateScore();
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Score
|
||||||
function updateScore() {
|
function updateScore() {
|
||||||
const currentScore = snake.length - 1;
|
score.textContent = (snake.length - 1).toString().padStart(3, '0');
|
||||||
score.textContent = currentScore.toString().padStart(3, '0');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopGame() {
|
function stopGame() {
|
||||||
clearInterval(gameInterval);
|
clearInterval(gameInterval);
|
||||||
gameStarted = false;
|
gameStarted = false;
|
||||||
instructionText.style.display = 'block';
|
instructionText.style.display = 'block';
|
||||||
logo.style.display = 'block';
|
logo.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHighScore() {
|
function updateHighScore() {
|
||||||
const currentScore = snake.length - 1;
|
const currentScore = snake.length - 1;
|
||||||
if (currentScore > highScore) {
|
if (currentScore > highScore) {
|
||||||
|
|||||||
65
style.css
65
style.css
@@ -5,53 +5,31 @@ body {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'VT323', monospace;
|
font-family: 'VT323', monospace;
|
||||||
}
|
|
||||||
|
|
||||||
body,
|
|
||||||
.snake {
|
|
||||||
background-color: #414141;
|
background-color: #414141;
|
||||||
}
|
}
|
||||||
|
|
||||||
#game-board {
|
.container {
|
||||||
border-radius: 100px;
|
position: relative;
|
||||||
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;
|
|
||||||
text-align: center;
|
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 {
|
.scores {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
width: 400px;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#score {
|
#score {
|
||||||
@@ -70,20 +48,21 @@ body,
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-border-3,
|
#instruction-text {
|
||||||
#logo {
|
color: #333;
|
||||||
background-color: #c4cfa3;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snake {
|
.snake {
|
||||||
|
background-color: #414141;
|
||||||
border: #5a5a5a 1px dotted;
|
border: #5a5a5a 1px dotted;
|
||||||
}
|
}
|
||||||
|
|
||||||
.food {
|
.food {
|
||||||
background-color: #dedede;
|
background-color: #dedede;
|
||||||
border: #999 5px solid;
|
border: #999 2px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
position: absolute;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user