I feel like there's something wrong with the way my code is styled but I can't quite place what's wrong with it. Perhaps the way I've represented the snake is a little convoluted?
const HEIGHT = 20;
const WIDTH = 20;
const snake = function() {
const snakeCoordinates = [[0, 0], [1, 0]];
const moveDirections = [[-1, 0], [-1, 0]];
function shiftDirections() {
for (let i = snakeCoordinates.length; i > 0; i--)
moveDirections[i] = moveDirections[i - 1];
}
return {
hasSnake: function(row, col) {
return snakeCoordinates.reduce((accum, [snakeRow, snakeCol]) => accum || (row === snakeRow && col === snakeCol), false);
},
expandSnake: function() {
const head = snakeCoordinates[0];
const headDirection = moveDirections[0];
snakeCoordinates.unshift(wrapAround([head[0] + headDirection[0], head[1] + headDirection[1]]));
moveDirections.unshift(headDirection);
},
changeDirection: function(x, y) {
const isAboutTurn = x === -moveDirections[0][0] || y === -moveDirections[0][1];
if (!isAboutTurn)
moveDirections[0] = [x, y];
},
getSnakeHead: function() {
return snakeCoordinates[0];
},
isSnakeDead: function() {
const tail = snakeCoordinates.slice(1);
const head = snakeCoordinates[0];
return tail.reduce((accum, [row, col]) => accum || (row === head[0] && col === head[1]), false);
},
moveSnake: function() {
for (let i = 0; i < snakeCoordinates.length; i++) {
snakeCoordinates[i][0] += moveDirections[i][0];
snakeCoordinates[i][1] += moveDirections[i][1];
snakeCoordinates[i] = wrapAround(snakeCoordinates[i]);
}
shiftDirections();
}
};
}();
const goal = function() {
var goal = [HEIGHT - 1, WIDTH - 1];
return {
newGoal: function() {
goal = [rng(0, HEIGHT), rng(0, WIDTH)];
},
isGoal: function(row, col) {
return goal[0] === row && goal[1] === col;
}
};
}();
initialiseGame();
function initialiseGame() {
initialiseGridInDOM();
addEventListeners();
runGame();
}
function addEventListeners() {
document.addEventListener("keydown", dealWithKeyPress);
}
// Map key press onto action
function dealWithKeyPress(keyPress) {
const leftArrow = 37;
const upArrow = 38;
const rightArrow = 39;
const downArrow = 40;
switch (keyPress.keyCode) {
case upArrow:
snake.changeDirection(-1, 0);
break;
case leftArrow:
snake.changeDirection(0, -1);
break;
case rightArrow:
snake.changeDirection(0, 1);
break;
case downArrow:
snake.changeDirection(1, 0);
break;
}
}
async function runGame() {
var running = true;
while (running) {
snake.moveSnake();
checkForSnakeDeath(gameOver);
checkForGoalCapture();
updateDOM();
await delay();
}
function gameOver() {
alert("Game over!");
running = false;
}
}
function checkForSnakeDeath(gameOver) {
if (snake.isSnakeDead())
gameOver();
}
function checkForGoalCapture() {
const head = snake.getSnakeHead();
if (goal.isGoal(head[0], head[1])) {
snake.expandSnake();
goal.newGoal();
}
}
function delay() {
return new Promise(resolve => {
setTimeout(resolve, 60);
});
}
function updateDOM() {
for (let row = 0; row < HEIGHT; row++)
for (let col = 0; col < WIDTH; col++)
if (snake.hasSnake(row, col))
colorSnakeInDOM(row, col);
else if (goal.isGoal(row, col))
colorGoalInDOM(row, col);
else
colorWallInDOM(row, col);
}
function colorSnakeInDOM(row, col) {
const SNAKE_COLOR = "#00BFFF";
colorTileInDOM(SNAKE_COLOR, row, col);
}
function colorWallInDOM(row, col) {
const WALL_COLOR = selectBackgroundColor(row, col);
colorTileInDOM(WALL_COLOR, row, col);
}
function colorGoalInDOM(row, col) {
const GOAL_COLOR = "yellow";
colorTileInDOM(GOAL_COLOR, row, col);
}
function colorTileInDOM(color, row, col) {
const tileDOM = getTileInDOM(row, col);
tileDOM.style.backgroundColor = color;
}
function getTileInDOM(row, col) {
const gridDOM = document.querySelector("#grid");
const rowDOM = gridDOM.rows[row];
const tileDOM = rowDOM.cells[col];
return tileDOM;
}
// Dynamically generate HTML for a plain grid
function initialiseGridInDOM() {
const gridDOM = document.querySelector("#grid");
for (let row = 0; row < HEIGHT; row++) {
let newRow = createEmptyRowInDOM(row);
gridDOM.append(newRow);
}
updateDOM();
}
function createEmptyRowInDOM(row) {
const newRow = document.createElement("tr");
newRow.className = "row";
for (let col = 0; col < WIDTH; col++) {
let newTile = createEmptyTileInDOM(row, col);
newRow.append(newTile);
}
return newRow;
}
function createEmptyTileInDOM(row, col) {
const newTile = document.createElement("td");
newTile.className = "tile";
return newTile;
}
function selectBackgroundColor(row, col) {
const BACKGROUND_COLOR1 = "#3dfc03";
const BACKGROUND_COLOR2 = "#03fc03";
if ((row + col) % 2 === 0)
return BACKGROUND_COLOR1;
else
return BACKGROUND_COLOR2;
}
function wrapAround([row, col]) {
if (row === HEIGHT)
row = 0;
if (row < 0)
row = HEIGHT - 1;
if (col === WIDTH)
col = 0;
if (col < 0)
col = WIDTH - 1;
return [row, col];
}
// Generates a random number whose value lies between lower and upper
function rng(lower, upper) {
return Math.floor(Math.random() * (upper - lower)) + lower;
}
```