├── .gitattributes ├── index.html ├── script.js ├── snake-game-ai-gen.png └── style.css /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | Snake Game 13 | 14 | 15 | 16 | 17 |
18 |
19 |

000

20 |

000

21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |

Press spacebar to start the game

31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | // Define HTML elements 2 | const board = document.getElementById('game-board'); 3 | const instructionText = document.getElementById('instruction-text'); 4 | const logo = document.getElementById('logo'); 5 | const score = document.getElementById('score'); 6 | const highScoreText = document.getElementById('highScore'); 7 | 8 | // Define game variables 9 | const gridSize = 20; 10 | let snake = [{ x: 10, y: 10 }]; 11 | let food = generateFood(); 12 | let highScore = 0; 13 | let direction = 'right'; 14 | let gameInterval; 15 | let gameSpeedDelay = 200; 16 | let gameStarted = false; 17 | 18 | // Draw game map, snake, food 19 | function draw() { 20 | board.innerHTML = ''; 21 | drawSnake(); 22 | drawFood(); 23 | updateScore(); 24 | } 25 | 26 | // Draw snake 27 | function drawSnake() { 28 | snake.forEach((segment) => { 29 | const snakeElement = createGameElement('div', 'snake'); 30 | setPosition(snakeElement, segment); 31 | board.appendChild(snakeElement); 32 | }); 33 | } 34 | 35 | // Create a snake or food cube/div 36 | function createGameElement(tag, className) { 37 | const element = document.createElement(tag); 38 | element.className = className; 39 | return element; 40 | } 41 | 42 | // Set the position of snake or food 43 | function setPosition(element, position) { 44 | element.style.gridColumn = position.x; 45 | element.style.gridRow = position.y; 46 | } 47 | 48 | // Testing draw function 49 | // draw(); 50 | 51 | // Draw food function 52 | function drawFood() { 53 | if (gameStarted) { 54 | const foodElement = createGameElement('div', 'food'); 55 | setPosition(foodElement, food); 56 | board.appendChild(foodElement); 57 | } 58 | } 59 | 60 | // Generate food 61 | function generateFood() { 62 | const x = Math.floor(Math.random() * gridSize) + 1; 63 | const y = Math.floor(Math.random() * gridSize) + 1; 64 | return { x, y }; 65 | } 66 | 67 | // Moving the snake 68 | function move() { 69 | const head = { ...snake[0] }; 70 | switch (direction) { 71 | case 'up': 72 | head.y--; 73 | break; 74 | case 'down': 75 | head.y++; 76 | break; 77 | case 'left': 78 | head.x--; 79 | break; 80 | case 'right': 81 | head.x++; 82 | break; 83 | } 84 | 85 | snake.unshift(head); 86 | 87 | // snake.pop(); 88 | 89 | if (head.x === food.x && head.y === food.y) { 90 | food = generateFood(); 91 | increaseSpeed(); 92 | clearInterval(gameInterval); // Clear past interval 93 | gameInterval = setInterval(() => { 94 | move(); 95 | checkCollision(); 96 | draw(); 97 | }, gameSpeedDelay); 98 | } else { 99 | snake.pop(); 100 | } 101 | } 102 | 103 | // Test moving 104 | // setInterval(() => { 105 | // move(); // Move first 106 | // draw(); // Then draw again new position 107 | // }, 200); 108 | 109 | // Start game function 110 | function startGame() { 111 | gameStarted = true; // Keep track of a running game 112 | instructionText.style.display = 'none'; 113 | logo.style.display = 'none'; 114 | gameInterval = setInterval(() => { 115 | move(); 116 | checkCollision(); 117 | draw(); 118 | }, gameSpeedDelay); 119 | } 120 | 121 | // Keypress event listener 122 | function handleKeyPress(event) { 123 | if ( 124 | (!gameStarted && event.code === 'Space') || 125 | (!gameStarted && event.key === ' ') 126 | ) { 127 | startGame(); 128 | } else { 129 | switch (event.key) { 130 | case 'ArrowUp': 131 | direction = 'up'; 132 | break; 133 | case 'ArrowDown': 134 | direction = 'down'; 135 | break; 136 | case 'ArrowLeft': 137 | direction = 'left'; 138 | break; 139 | case 'ArrowRight': 140 | direction = 'right'; 141 | break; 142 | } 143 | } 144 | } 145 | 146 | document.addEventListener('keydown', handleKeyPress); 147 | 148 | function increaseSpeed() { 149 | // console.log(gameSpeedDelay); 150 | if (gameSpeedDelay > 150) { 151 | gameSpeedDelay -= 5; 152 | } else if (gameSpeedDelay > 100) { 153 | gameSpeedDelay -= 3; 154 | } else if (gameSpeedDelay > 50) { 155 | gameSpeedDelay -= 2; 156 | } else if (gameSpeedDelay > 25) { 157 | gameSpeedDelay -= 1; 158 | } 159 | } 160 | 161 | function checkCollision() { 162 | const head = snake[0]; 163 | 164 | if (head.x < 1 || head.x > gridSize || head.y < 1 || head.y > gridSize) { 165 | resetGame(); 166 | } 167 | 168 | for (let i = 1; i < snake.length; i++) { 169 | if (head.x === snake[i].x && head.y === snake[i].y) { 170 | resetGame(); 171 | } 172 | } 173 | } 174 | 175 | function resetGame() { 176 | updateHighScore(); 177 | stopGame(); 178 | snake = [{ x: 10, y: 10 }]; 179 | food = generateFood(); 180 | direction = 'right'; 181 | gameSpeedDelay = 200; 182 | updateScore(); 183 | } 184 | 185 | function updateScore() { 186 | const currentScore = snake.length - 1; 187 | score.textContent = currentScore.toString().padStart(3, '0'); 188 | } 189 | 190 | function stopGame() { 191 | clearInterval(gameInterval); 192 | gameStarted = false; 193 | instructionText.style.display = 'block'; 194 | logo.style.display = 'block'; 195 | } 196 | 197 | function updateHighScore() { 198 | const currentScore = snake.length - 1; 199 | if (currentScore > highScore) { 200 | highScore = currentScore; 201 | highScoreText.textContent = highScore.toString().padStart(3, '0'); 202 | } 203 | highScoreText.style.display = 'block'; 204 | } 205 | -------------------------------------------------------------------------------- /snake-game-ai-gen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ade-mir/snake-game-js/9fab4a6a9dbf6eff1563a7f9b3d76e51b97edbce/snake-game-ai-gen.png -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | height: 100vh; 6 | margin: 0; 7 | font-family: 'VT323', monospace; 8 | } 9 | 10 | body, 11 | .snake { 12 | background-color: #414141; 13 | } 14 | 15 | #game-board { 16 | border-radius: 100px; 17 | display: grid; 18 | grid-template-columns: repeat(20, 20px); 19 | grid-template-rows: repeat(20, 20px); 20 | margin: 5px; 21 | } 22 | 23 | .game-border-1 { 24 | border: #595f43 solid 10px; 25 | border-radius: 30px; 26 | box-shadow: inset 0 0 0 10px #595f43; 27 | } 28 | 29 | .game-border-2 { 30 | border: #abb78a solid 8px; 31 | border-radius: 26px; 32 | box-shadow: inset 0 0 0 10px #abb78a; 33 | } 34 | 35 | .game-border-3 { 36 | border: #8b966c solid 30px; 37 | border-radius: 20px; 38 | box-shadow: inset 0 0 0 5px #8b966c; 39 | } 40 | 41 | #instruction-text { 42 | position: absolute; 43 | top: 60%; 44 | color: #333; 45 | width: 300px; 46 | text-align: center; 47 | text-transform: capitalize; 48 | padding: 30px; 49 | margin: 0; 50 | } 51 | 52 | .scores { 53 | display: flex; 54 | justify-content: space-between; 55 | } 56 | 57 | #score { 58 | color: #abb78a; 59 | } 60 | 61 | #score, 62 | #highScore { 63 | font-size: 40px; 64 | font-weight: bolder; 65 | margin: 10px 0; 66 | } 67 | 68 | #highScore { 69 | color: #d8ddca; 70 | display: none; 71 | } 72 | 73 | .game-border-3, 74 | #logo { 75 | background-color: #c4cfa3; 76 | } 77 | 78 | .snake { 79 | border: #5a5a5a 1px dotted; 80 | } 81 | 82 | .food { 83 | background-color: #dedede; 84 | border: #999 5px solid; 85 | } 86 | 87 | #logo { 88 | position: absolute; 89 | } 90 | --------------------------------------------------------------------------------