|
| 1 | +const SnakeGame = function() { |
| 2 | + const CANVAS_BORDER_COLOUR = 'black'; |
| 3 | + const CANVAS_BACKGROUND_COLOUR = 'white'; |
| 4 | + const SNAKE_FILL_COLOUR = 'lightGreen'; |
| 5 | + const SNAKE_BORDER_COLOUR = 'darkGreen'; |
| 6 | + const FOOD_FILL_COLOUR = 'red'; |
| 7 | + const FOOD_BORDER_COLOUR = 'darkRed'; |
| 8 | + const GAME_CANVAS = document.getElementById('gameCanvas'); |
| 9 | + const CTX = GAME_CANVAS.getContext('2d'); |
| 10 | + |
| 11 | + let score; |
| 12 | + let snakeSpeed; |
| 13 | + let gameLoop; |
| 14 | + let snake; |
| 15 | + let velocityX; |
| 16 | + let velocityY; |
| 17 | + let foodX; |
| 18 | + let foodY; |
| 19 | + let changingDirection; |
| 20 | + let inProgress; |
| 21 | + let changeSpeedValue; |
| 22 | + let gridCellSize; |
| 23 | + let foodScoreValue; |
| 24 | + |
| 25 | + const initialize = () => { |
| 26 | + score = 0; |
| 27 | + snakeSpeed = 200; |
| 28 | + snake = [ |
| 29 | + { x: 150, y: 150 }, |
| 30 | + { x: 140, y: 150 }, |
| 31 | + { x: 130, y: 150 }, |
| 32 | + { x: 120, y: 150 }, |
| 33 | + { x: 110, y: 150 } |
| 34 | + ]; |
| 35 | + velocityX = 10; |
| 36 | + velocityY = 0; |
| 37 | + changingDirection = false; |
| 38 | + inProgress = true; |
| 39 | + changeSpeedValue = 10; |
| 40 | + gridCellSize = 10; |
| 41 | + foodScoreValue = 10; |
| 42 | + updateGameData(); |
| 43 | + }; |
| 44 | + |
| 45 | + //create game elements |
| 46 | + const setCanvas = () => { |
| 47 | + CTX.fillStyle = CANVAS_BACKGROUND_COLOUR; |
| 48 | + CTX.strokeStyle = CANVAS_BORDER_COLOUR; |
| 49 | + CTX.fillRect(0, 0, GAME_CANVAS.width, GAME_CANVAS.height); |
| 50 | + CTX.strokeRect(0, 0, GAME_CANVAS.width, GAME_CANVAS.height); |
| 51 | + }; |
| 52 | + |
| 53 | + const drawSnakeUnit = snakeUnit => { |
| 54 | + CTX.fillStyle = SNAKE_FILL_COLOUR; |
| 55 | + CTX.strokeStyle = SNAKE_BORDER_COLOUR; |
| 56 | + CTX.fillRect(snakeUnit.x, snakeUnit.y, gridCellSize, gridCellSize); |
| 57 | + CTX.strokeRect(snakeUnit.x, snakeUnit.y, gridCellSize, gridCellSize); |
| 58 | + }; |
| 59 | + |
| 60 | + const drawFood = () => { |
| 61 | + CTX.fillStyle = FOOD_FILL_COLOUR; |
| 62 | + CTX.strokeStyle = FOOD_BORDER_COLOUR; |
| 63 | + CTX.fillRect(foodX, foodY, gridCellSize, gridCellSize); |
| 64 | + CTX.strokeRect(foodX, foodY, gridCellSize, gridCellSize); |
| 65 | + }; |
| 66 | + |
| 67 | + //food logic |
| 68 | + const randomMultipleOfTen = (min, max) => { |
| 69 | + return ( |
| 70 | + Math.round((Math.random() * (max - min) + min) / gridCellSize) * |
| 71 | + gridCellSize |
| 72 | + ); |
| 73 | + }; |
| 74 | + |
| 75 | + const makeFood = () => { |
| 76 | + foodX = randomMultipleOfTen(0, GAME_CANVAS.width - gridCellSize); |
| 77 | + foodY = randomMultipleOfTen(0, GAME_CANVAS.height - gridCellSize); |
| 78 | + |
| 79 | + snake.forEach(function isFoodOnSnake(part) { |
| 80 | + const foodIsOnSnake = part.x == foodX && part.y == foodY; |
| 81 | + if (foodIsOnSnake) makeFood(); |
| 82 | + }); |
| 83 | + }; |
| 84 | + |
| 85 | + //snake logic |
| 86 | + const drawSnake = () => { |
| 87 | + snake.forEach(drawSnakeUnit); |
| 88 | + }; |
| 89 | + const moveSnake = () => { |
| 90 | + const head = { x: snake[0].x + velocityX, y: snake[0].y + velocityY }; |
| 91 | + snake.unshift(head); |
| 92 | + |
| 93 | + const snakeAteFood = snake[0].x === foodX && snake[0].y === foodY; |
| 94 | + if (snakeAteFood) { |
| 95 | + score += foodScoreValue; |
| 96 | + snakeSpeed -= changeSpeedValue; |
| 97 | + updateGameData(); |
| 98 | + makeFood(); |
| 99 | + } else { |
| 100 | + snake.pop(); |
| 101 | + } |
| 102 | + }; |
| 103 | + const changeDirection = event => { |
| 104 | + const keyPressed = event.code; |
| 105 | + const movingUp = velocityY === -gridCellSize; |
| 106 | + const movingDown = velocityY === gridCellSize; |
| 107 | + const movingRight = velocityX === gridCellSize; |
| 108 | + const movingLeft = velocityX === -gridCellSize; |
| 109 | + |
| 110 | + if (changingDirection) return; |
| 111 | + changingDirection = true; |
| 112 | + |
| 113 | + if (keyPressed === 'ArrowLeft' && !movingRight) { |
| 114 | + event.preventDefault(); |
| 115 | + velocityX = -gridCellSize; |
| 116 | + velocityY = 0; |
| 117 | + } else if (keyPressed === 'ArrowUp' && !movingDown) { |
| 118 | + event.preventDefault(); |
| 119 | + velocityX = 0; |
| 120 | + velocityY = -gridCellSize; |
| 121 | + } else if (keyPressed === 'ArrowRight' && !movingLeft) { |
| 122 | + event.preventDefault(); |
| 123 | + velocityX = gridCellSize; |
| 124 | + velocityY = 0; |
| 125 | + } else if (keyPressed === 'ArrowDown' && !movingUp) { |
| 126 | + event.preventDefault(); |
| 127 | + velocityX = 0; |
| 128 | + velocityY = gridCellSize; |
| 129 | + } |
| 130 | + }; |
| 131 | + |
| 132 | + //game logic |
| 133 | + const didGameEnd = () => { |
| 134 | + // init i at 4 because first 4 units of snake cannot collide with each other |
| 135 | + for (let i = 4; i < snake.length; i++) { |
| 136 | + const didCollide = snake[i].x === snake[0].x && snake[i].y === snake[0].y; |
| 137 | + if (didCollide) return true; |
| 138 | + } |
| 139 | + |
| 140 | + const hitLeftBorder = snake[0].x < 0; |
| 141 | + const hitRightBorder = snake[0].x > GAME_CANVAS.width - gridCellSize; |
| 142 | + const hitTopBorder = snake[0].y < 0; |
| 143 | + const hitBottomBorder = snake[0].y > GAME_CANVAS.height - gridCellSize; |
| 144 | + |
| 145 | + return hitLeftBorder || hitRightBorder || hitTopBorder || hitBottomBorder; |
| 146 | + }; |
| 147 | + const updateGameData = () => { |
| 148 | + document.getElementById('score').innerHTML = score; |
| 149 | + document.getElementById('speed').innerHTML = snakeSpeed; |
| 150 | + }; |
| 151 | + const gameEngine = () => { |
| 152 | + if (didGameEnd()) { |
| 153 | + alert('You died!'); |
| 154 | + clearInterval(gameLoop); |
| 155 | + inProgress = false; |
| 156 | + return; |
| 157 | + } |
| 158 | + |
| 159 | + gameLoop = setTimeout(function() { |
| 160 | + changingDirection = false; |
| 161 | + setCanvas(); |
| 162 | + drawSnake(); |
| 163 | + drawFood(); |
| 164 | + moveSnake(); |
| 165 | + gameEngine(); |
| 166 | + }, snakeSpeed); |
| 167 | + }; |
| 168 | + |
| 169 | + document.addEventListener('keydown', changeDirection); |
| 170 | + document.getElementById('startGame').addEventListener('click', function() { |
| 171 | + if (!inProgress) { |
| 172 | + initialize(); |
| 173 | + gameEngine(); |
| 174 | + makeFood(); |
| 175 | + } else { |
| 176 | + return; |
| 177 | + } |
| 178 | + }); |
| 179 | +}; |
| 180 | +SnakeGame(); |
0 commit comments