├── .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 |
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 |
--------------------------------------------------------------------------------