if (!window.SNAKE) {
window.SNAKE = {};
}
const MOVE_NONE = -1;
const MOVE_UP = 0;
const MOVE_LEFT = 3;
const MOVE_DOWN = 2;
const MOVE_RIGHT = 1;
const MIN_SNAKE_SPEED = 25;
const RUSH_INCR = 5;
const DEFAULT_SNAKE_SPEED = 80;
const BOARD_NOT_READY = 0;
const BOARD_READY = 1;
const BOARD_IN_PLAY = 2;
const HIGH_SCORE_KEY = "jsSnakeHighScore";
SNAKE.addEventListener = (function () {
if (window.addEventListener) {
return function (obj, event, funct, evtCapturing) {
obj.addEventListener(event, funct, evtCapturing);
};
} else if (window.attachEvent) {
return function (obj, event, funct) {
obj.attachEvent("on" + event, funct);
};
}
})();
SNAKE.removeEventListener = (function () {
if (window.removeEventListener) {
return function (obj, event, funct, evtCapturing) {
obj.removeEventListener(event, funct, evtCapturing);
};
} else if (window.detachEvent) {
return function (obj, event, funct) {
obj.detachEvent("on" + event, funct);
};
}
})();
SNAKE.Snake =
SNAKE.Snake ||
(function () {
const blockPool = [];
const SnakeBlock = function () {
this.elm = null;
this.elmStyle = null;
this.row = -1;
this.col = -1;
this.next = null;
this.prev = null;
};
function getNextHighestZIndex(myObj) {
let highestIndex = 0,
currentIndex = 0,
ii;
for (ii in myObj) {
if (myObj[ii].elm.currentStyle) {
currentIndex = parseFloat(myObj[ii].elm.style["z-index"], 10);
} else if (window.getComputedStyle) {
currentIndex = parseFloat(
document.defaultView
.getComputedStyle(myObj[ii].elm, null)
.getPropertyValue("z-index"),
10,
);
}
if (!isNaN(currentIndex) && currentIndex > highestIndex) {
highestIndex = currentIndex;
}
}
return highestIndex + 1;
}
return function (config) {
if (!config || !config.playingBoard) {
return;
}
if (localStorage[HIGH_SCORE_KEY] === undefined)
localStorage.setItem(HIGH_SCORE_KEY, 0);
const me = this;
const playingBoard = config.playingBoard;
const growthIncr = 5;
const columnShift = [0, 1, 0, -1];
const rowShift = [-1, 0, 1, 0];
let prevNode;
let lastMove = 1,
preMove = MOVE_NONE,
isFirstGameMove = true,
currentDirection = MOVE_NONE,
snakeSpeed = DEFAULT_SNAKE_SPEED,
isDead = false,
isPaused = false;
const modeDropdown = document.getElementById("selectMode");
if (modeDropdown) {
modeDropdown.addEventListener("change", function (evt) {
evt = evt || {};
let val = evt.target
? parseInt(evt.target.value)
: DEFAULT_SNAKE_SPEED;
if (isNaN(val)) {
val = DEFAULT_SNAKE_SPEED;
} else if (val < MIN_SNAKE_SPEED) {
val = DEFAULT_SNAKE_SPEED;
}
snakeSpeed = val;
setTimeout(function () {
document.getElementById("game-area").focus();
}, 10);
});
}
me.snakeBody = {};
me.snakeBody["b0"] = new SnakeBlock();
me.snakeBody["b0"].row = config.startRow || 1;
me.snakeBody["b0"].col = config.startCol || 1;
me.snakeBody["b0"].elm = createSnakeElement();
me.snakeBody["b0"].elmStyle = me.snakeBody["b0"].elm.style;
playingBoard.getBoardContainer().appendChild(me.snakeBody["b0"].elm);
me.snakeBody["b0"].elm.style.left = getLeftPosition(me.snakeBody["b0"]);
me.snakeBody["b0"].elm.style.top = getTopPosition(me.snakeBody["b0"]);
me.snakeBody["b0"].next = me.snakeBody["b0"];
me.snakeBody["b0"].prev = me.snakeBody["b0"];
me.snakeLength = 1;
me.snakeHead = me.snakeBody["b0"];
me.snakeTail = me.snakeBody["b0"];
me.snakeHead.elm.className = me.snakeHead.elm.className.replace(
/\bsnake-snakebody-dead\b/,
"",
);
me.snakeHead.elm.id = "snake-snakehead-alive";
me.snakeHead.elm.className += " snake-snakebody-alive";
function getTopPosition(block) {
const num = block.row * playingBoard.getBlockHeight();
return `${num}px`;
}
function getLeftPosition(block) {
const num = block.col * playingBoard.getBlockWidth();
return `${num}px`;
}
function createSnakeElement() {
const tempNode = document.createElement("div");
tempNode.className = "snake-snakebody-block";
tempNode.style.left = "-1000px";
tempNode.style.top = "-1000px";
tempNode.style.width = playingBoard.getBlockWidth() + "px";
tempNode.style.height = playingBoard.getBlockHeight() + "px";
return tempNode;
}
function createBlocks(num) {
let tempBlock;
const tempNode = createSnakeElement();
for (let ii = 1; ii < num; ii++) {
tempBlock = new SnakeBlock();
tempBlock.elm = tempNode.cloneNode(true);
tempBlock.elmStyle = tempBlock.elm.style;
playingBoard.getBoardContainer().appendChild(tempBlock.elm);
blockPool[blockPool.length] = tempBlock;
}
tempBlock = new SnakeBlock();
tempBlock.elm = tempNode;
playingBoard.getBoardContainer().appendChild(tempBlock.elm);
blockPool[blockPool.length] = tempBlock;
}
function recordScore() {
const highScore = localStorage[HIGH_SCORE_KEY];
if (me.snakeLength > highScore) {
alert(
"Congratulations! You have beaten your previous high score, which was " +
highScore +
".",
);
localStorage.setItem(HIGH_SCORE_KEY, me.snakeLength);
}
}
function handleEndCondition(handleFunc) {
recordScore();
me.snakeHead.elm.style.zIndex = getNextHighestZIndex(me.snakeBody);
me.snakeHead.elm.className = me.snakeHead.elm.className.replace(
/\bsnake-snakebody-alive\b/,
"",
);
me.snakeHead.elm.className += " snake-snakebody-dead";
isDead = true;
handleFunc();
}
me.setPaused = function (val) {
isPaused = val;
};
me.getPaused = function () {
return isPaused;
};
me.setDirection = (direction) => {
if (currentDirection !== lastMove) {
preMove = direction;
}
if (Math.abs(direction - lastMove) !== 2 || isFirstGameMove) {
currentDirection = direction;
isFirstGameMove = false;
}
};
me.handleArrowKeys = function (keyNum) {
if (isDead || (isPaused && !config.premoveOnPause)) {
return;
}
let directionFound = MOVE_NONE;
switch (keyNum) {
case 37:
case 65:
directionFound = MOVE_LEFT;
break;
case 38:
case 87:
directionFound = MOVE_UP;
break;
case 39:
case 68:
directionFound = MOVE_RIGHT;
break;
case 40:
case 83:
directionFound = MOVE_DOWN;
break;
}
me.setDirection(directionFound);
};
me.go = function () {
const oldHead = me.snakeHead,
newHead = me.snakeTail,
grid = playingBoard.grid;
if (isPaused === true) {
setTimeout(function () {
me.go();
}, snakeSpeed);
return;
}
if (config.moveSnakeWithAI) {
config.moveSnakeWithAI({
grid,
snakeHead: me.snakeHead,
currentDirection,
isFirstGameMove,
setDirection: me.setDirection,
});
}
me.snakeTail = newHead.prev;
me.snakeHead = newHead;
if (grid[newHead.row] && grid[newHead.row][newHead.col]) {
grid[newHead.row][newHead.col] = 0;
}
if (currentDirection !== MOVE_NONE) {
lastMove = currentDirection;
if (preMove !== MOVE_NONE) {
currentDirection = preMove;
preMove = MOVE_NONE;
}
}
newHead.col = oldHead.col + columnShift[lastMove];
newHead.row = oldHead.row + rowShift[lastMove];
if (!newHead.elmStyle) {
newHead.elmStyle = newHead.elm.style;
}
newHead.elmStyle.left = getLeftPosition(newHead);
newHead.elmStyle.top = getTopPosition(newHead);
if (me.snakeLength > 1) {
newHead.elm.id = "snake-snakehead-alive";
oldHead.elm.id = "";
}
if (grid[newHead.row][newHead.col] === 0) {
grid[newHead.row][newHead.col] = 1;
setTimeout(function () {
me.go();
}, snakeSpeed);
} else if (grid[newHead.row][newHead.col] > 0) {
me.handleDeath();
} else if (
grid[newHead.row][newHead.col] === playingBoard.getGridFoodValue()
) {
grid[newHead.row][newHead.col] = 1;
if (!me.eatFood()) {
me.handleWin();
return;
}
setTimeout(function () {
me.go();
}, snakeSpeed);
}
};
me.eatFood = function () {
if (blockPool.length <= growthIncr) {
createBlocks(growthIncr * 2);
}
const blocks = blockPool.splice(0, growthIncr);
let ii = blocks.length,
index;
prevNode = me.snakeTail;
while (ii--) {
index = "b" + me.snakeLength++;
me.snakeBody[index] = blocks[ii];
me.snakeBody[index].prev = prevNode;
me.snakeBody[index].elm.className =
me.snakeHead.elm.className.replace(/\bsnake-snakebody-dead\b/, "");
me.snakeBody[index].elm.className += " snake-snakebody-alive";
prevNode.next = me.snakeBody[index];
prevNode = me.snakeBody[index];
}
me.snakeTail = me.snakeBody[index];
me.snakeTail.next = me.snakeHead;
me.snakeHead.prev = me.snakeTail;
if (!playingBoard.foodEaten()) {
return false;
}
const selectDropDown = document.getElementById("selectMode");
const selectedOption =
selectDropDown.options[selectDropDown.selectedIndex];
if (selectedOption.text.localeCompare("Rush") == 0) {
if (snakeSpeed > MIN_SNAKE_SPEED + RUSH_INCR) {
snakeSpeed -= RUSH_INCR;
}
}
return true;
};
me.handleDeath = function () {
const selectedSpeed = document.getElementById("selectMode").value;
snakeSpeed = parseInt(selectedSpeed);
handleEndCondition(playingBoard.handleDeath);
};
me.handleWin = function () {
handleEndCondition(playingBoard.handleWin);
};
me.rebirth = function () {
isDead = false;
isFirstGameMove = true;
preMove = MOVE_NONE;
};
me.reset = function () {
if (isDead === false) {
return;
}
const blocks = [];
let curNode = me.snakeHead.next;
let nextNode;
while (curNode !== me.snakeHead) {
nextNode = curNode.next;
curNode.prev = null;
curNode.next = null;
blocks.push(curNode);
curNode = nextNode;
}
me.snakeHead.next = me.snakeHead;
me.snakeHead.prev = me.snakeHead;
me.snakeTail = me.snakeHead;
me.snakeLength = 1;
for (let ii = 0; ii < blocks.length; ii++) {
blocks[ii].elm.style.left = "-1000px";
blocks[ii].elm.style.top = "-1000px";
blocks[ii].elm.className = me.snakeHead.elm.className.replace(
/\bsnake-snakebody-dead\b/,
"",
);
blocks[ii].elm.className += " snake-snakebody-alive";
}
blockPool.concat(blocks);
me.snakeHead.elm.className = me.snakeHead.elm.className.replace(
/\bsnake-snakebody-dead\b/,
"",
);
me.snakeHead.elm.className += " snake-snakebody-alive";
me.snakeHead.elm.id = "snake-snakehead-alive";
me.snakeHead.row = config.startRow || 1;
me.snakeHead.col = config.startCol || 1;
me.snakeHead.elm.style.left = getLeftPosition(me.snakeHead);
me.snakeHead.elm.style.top = getTopPosition(me.snakeHead);
};
me.getSpeed = () => {
return snakeSpeed;
};
me.setSpeed = (speed) => {
snakeSpeed = speed;
};
createBlocks(growthIncr * 2);
};
})();
SNAKE.Food =
SNAKE.Food ||
(function () {
let instanceNumber = 0;
function getRandomPosition(x, y) {
return Math.floor(Math.random() * (y + 1 - x)) + x;
}
return function (config) {
if (!config || !config.playingBoard) {
return;
}
const me = this;
const playingBoard = config.playingBoard;
let fRow, fColumn;
const myId = instanceNumber++;
const elmFood = document.createElement("div");
elmFood.setAttribute("id", "snake-food-" + myId);
elmFood.className = "snake-food-block";
elmFood.style.width = playingBoard.getBlockWidth() + "px";
elmFood.style.height = playingBoard.getBlockHeight() + "px";
elmFood.style.left = "-1000px";
elmFood.style.top = "-1000px";
playingBoard.getBoardContainer().appendChild(elmFood);
me.getFoodElement = function () {
return elmFood;
};
me.randomlyPlaceFood = function () {
if (
playingBoard.grid[fRow] &&
playingBoard.grid[fRow][fColumn] === playingBoard.getGridFoodValue()
) {
playingBoard.grid[fRow][fColumn] = 0;
}
let row = 0,
col = 0,
numTries = 0;
const maxRows = playingBoard.grid.length - 1;
const maxCols = playingBoard.grid[0].length - 1;
while (playingBoard.grid[row][col] !== 0) {
row = getRandomPosition(1, maxRows);
col = getRandomPosition(1, maxCols);
numTries++;
if (numTries > 20000) {
return false;
}
}
playingBoard.grid[row][col] = playingBoard.getGridFoodValue();
fRow = row;
fColumn = col;
elmFood.style.top = row * playingBoard.getBlockHeight() + "px";
elmFood.style.left = col * playingBoard.getBlockWidth() + "px";
return true;
};
};
})();
SNAKE.Board =
SNAKE.Board ||
(function () {
let instanceNumber = 0;
function getNextHighestZIndex(myObj) {
let highestIndex = 0,
currentIndex = 0,
ii;
for (ii in myObj) {
if (myObj[ii].elm.currentStyle) {
currentIndex = parseFloat(myObj[ii].elm.style["z-index"], 10);
} else if (window.getComputedStyle) {
currentIndex = parseFloat(
document.defaultView
.getComputedStyle(myObj[ii].elm, null)
.getPropertyValue("z-index"),
10,
);
}
if (!isNaN(currentIndex) && currentIndex > highestIndex) {
highestIndex = currentIndex;
}
}
return highestIndex + 1;
}
function getClientWidth() {
let myWidth = 0;
if (typeof window.innerWidth === "number") {
myWidth = window.innerWidth;
} else if (
document.documentElement &&
(document.documentElement.clientWidth ||
document.documentElement.clientHeight)
) {
myWidth = document.documentElement.clientWidth;
} else if (
document.body &&
(document.body.clientWidth || document.body.clientHeight)
) {
myWidth = document.body.clientWidth;
}
return myWidth;
}
function getClientHeight() {
let myHeight = 0;
if (typeof window.innerHeight === "number") {
myHeight = window.innerHeight;
} else if (
document.documentElement &&
(document.documentElement.clientWidth ||
document.documentElement.clientHeight)
) {
myHeight = document.documentElement.clientHeight;
} else if (
document.body &&
(document.body.clientWidth || document.body.clientHeight)
) {
myHeight = document.body.clientHeight;
}
return myHeight;
}
return function (inputConfig) {
const me = this;
const myId = instanceNumber++;
const config = inputConfig || {};
const MAX_BOARD_COLS = 250;
const MAX_BOARD_ROWS = 250;
const blockWidth = 20;
const blockHeight = 20;
const GRID_FOOD_VALUE = -1;
if (!config.onLengthUpdate) {
config.onLengthUpdate = () => {};
}
if (!config.onPauseToggle) {
config.onPauseToggle = () => {};
}
if (!config.onWin) {
config.onWin = () => {};
}
if (!config.onDeath) {
config.onDeath = () => {};
}
let myFood,
mySnake,
boardState = BOARD_READY,
myKeyListener,
myWindowListener,
isPaused = false;
let elmContainer,
elmPlayingField,
elmAboutPanel,
elmLengthPanel,
elmHighscorePanel,
elmWelcome,
elmTryAgain,
elmWin,
elmPauseScreen;
me.grid = [];
function getStartRow() {
return config.startRow || 2;
}
function getStartCol() {
return config.startCol || 2;
}
function createBoardElements() {
elmPlayingField = document.createElement("div");
elmPlayingField.setAttribute("id", "playingField");
elmPlayingField.className = "snake-playing-field";
SNAKE.addEventListener(
elmPlayingField,
"click",
function () {
elmContainer.focus();
},
false,
);
elmPauseScreen = document.createElement("div");
elmPauseScreen.className = "snake-pause-screen";
elmPauseScreen.innerHTML =
"<div style='padding:10px;'>[Paused]<p/>Press [space] to unpause.</div>";
elmAboutPanel = document.createElement("div");
elmAboutPanel.className = "snake-panel-component";
elmAboutPanel.innerHTML =
"<a href='http://patorjk.com/blog/software/' class='snake-link'>more patorjk.com apps</a> - <a href='https://github.com/patorjk/JavaScript-Snake' class='snake-link'>source code</a> - <a href='https://www.youtube.com/channel/UCpcCLm9y6CsjHUrCvJHYHUA' class='snake-link'>pat's youtube</a>";
elmLengthPanel = document.createElement("div");
elmLengthPanel.className = "snake-panel-component";
elmLengthPanel.innerHTML = "Length: 1";
elmHighscorePanel = document.createElement("div");
elmHighscorePanel.className = "snake-panel-component";
elmHighscorePanel.innerHTML =
"Highscore: " + (localStorage[HIGH_SCORE_KEY] || 0);
if (!config.moveSnakeWithAI) {
elmWelcome = createWelcomeElement();
elmTryAgain = createTryAgainElement();
elmWin = createWinElement();
}
SNAKE.addEventListener(
elmContainer,
"keyup",
function (evt) {
if (!evt) evt = window.event;
evt.cancelBubble = true;
if (evt.stopPropagation) {
evt.stopPropagation();
}
if (evt.preventDefault) {
evt.preventDefault();
}
return false;
},
false,
);
elmContainer.className = "snake-game-container";
elmPauseScreen.style.zIndex = 10000;
elmContainer.appendChild(elmPauseScreen);
elmContainer.appendChild(elmPlayingField);
elmContainer.appendChild(elmAboutPanel);
elmContainer.appendChild(elmLengthPanel);
elmContainer.appendChild(elmHighscorePanel);
if (!config.moveSnakeWithAI) {
elmContainer.appendChild(elmWelcome);
elmContainer.appendChild(elmTryAgain);
elmContainer.appendChild(elmWin);
}
mySnake = new SNAKE.Snake({
playingBoard: me,
startRow: getStartRow(),
startCol: getStartCol(),
premoveOnPause: config.premoveOnPause,
moveSnakeWithAI: config.moveSnakeWithAI,
});
myFood = new SNAKE.Food({ playingBoard: me });
if (elmWelcome) {
elmWelcome.style.zIndex = 1000;
}
}
function maxBoardWidth() {
return MAX_BOARD_COLS * me.getBlockWidth();
}
function maxBoardHeight() {
return MAX_BOARD_ROWS * me.getBlockHeight();
}
function createWelcomeElement() {
const tmpElm = document.createElement("div");
tmpElm.id = "sbWelcome" + myId;
tmpElm.className = "snake-welcome-dialog";
const welcomeTxt = document.createElement("div");
let fullScreenText = "";
if (config.fullScreen) {
fullScreenText = "On Windows, press F11 to play in Full Screen mode.";
}
welcomeTxt.innerHTML =
"JavaScript Snake<p></p>Use the <strong>arrow keys</strong> on your keyboard to play the game. " +
fullScreenText +
"<p></p>";
const welcomeStart = document.createElement("button");
welcomeStart.appendChild(document.createTextNode("Play Game"));
const loadGame = function () {
SNAKE.removeEventListener(window, "keyup", kbShortcut, false);
tmpElm.style.display = "none";
me.setBoardState(BOARD_READY);
me.getBoardContainer().focus();
};
const kbShortcut = function (evt) {
if (!evt) evt = window.event;
const keyNum = evt.which ? evt.which : evt.keyCode;
if (keyNum === 32 || keyNum === 13) {
loadGame();
}
};
SNAKE.addEventListener(window, "keyup", kbShortcut, false);
SNAKE.addEventListener(welcomeStart, "click", loadGame, false);
tmpElm.appendChild(welcomeTxt);
tmpElm.appendChild(welcomeStart);
return tmpElm;
}
function createGameEndElement(message, elmId, elmClassName) {
const tmpElm = document.createElement("div");
tmpElm.id = elmId + myId;
tmpElm.className = elmClassName;
const gameEndTxt = document.createElement("div");
gameEndTxt.innerHTML = "JavaScript Snake<p></p>" + message + "<p></p>";
const gameEndStart = document.createElement("button");
gameEndStart.appendChild(document.createTextNode("Play Again?"));
const reloadGame = function () {
tmpElm.style.display = "none";
me.resetBoard();
me.setBoardState(BOARD_READY);
me.getBoardContainer().focus();
};
const kbGameEndShortcut = function (evt) {
if (boardState !== 0 || tmpElm.style.display !== "block") {
return;
}
if (!evt) evt = window.event;
const keyNum = evt.which ? evt.which : evt.keyCode;
if (keyNum === 32 || keyNum === 13) {
reloadGame();
}
};
SNAKE.addEventListener(window, "keyup", kbGameEndShortcut, true);
SNAKE.addEventListener(gameEndStart, "click", reloadGame, false);
tmpElm.appendChild(gameEndTxt);
tmpElm.appendChild(gameEndStart);
return tmpElm;
}
function createTryAgainElement() {
return createGameEndElement(
"You died :(",
"sbTryAgain",
"snake-try-again-dialog",
);
}
function createWinElement() {
return createGameEndElement("You win! :D", "sbWin", "snake-win-dialog");
}
function handleEndCondition(elmDialog) {
const index = Math.max(
getNextHighestZIndex(mySnake.snakeBody),
getNextHighestZIndex({ tmp: { elm: myFood.getFoodElement() } }),
);
if (elmDialog) {
elmContainer.removeChild(elmDialog);
elmContainer.appendChild(elmDialog);
elmDialog.style.zIndex = index;
elmDialog.style.display = "block";
}
me.setBoardState(BOARD_NOT_READY);
}
me.setPaused = function (val) {
isPaused = val;
mySnake.setPaused(val);
if (isPaused) {
elmPauseScreen.style.display = "block";
} else {
elmPauseScreen.style.display = "none";
}
config.onPauseToggle(isPaused);
};
me.getPaused = function () {
return isPaused;
};
me.resetBoard = function () {
SNAKE.removeEventListener(
elmContainer,
"keydown",
myKeyListener,
false,
);
SNAKE.removeEventListener(
elmContainer,
"visibilitychange",
myWindowListener,
false,
);
mySnake.reset();
config.onLengthUpdate(1);
elmLengthPanel.innerHTML = "Length: 1";
me.setupPlayingField();
me.grid[getStartRow()][getStartCol()] = 1;
};
me.getBoardState = function () {
return boardState;
};
me.setBoardState = function (state) {
boardState = state;
};
me.getGridFoodValue = function () {
return GRID_FOOD_VALUE;
};
me.getPlayingFieldElement = function () {
return elmPlayingField;
};
me.setBoardContainer = function (myContainer) {
if (typeof myContainer === "string") {
myContainer = document.getElementById(myContainer);
}
if (myContainer === elmContainer) {
return;
}
elmContainer = myContainer;
elmPlayingField = null;
me.setupPlayingField();
me.grid[getStartRow()][getStartCol()] = 1;
};
me.getBoardContainer = function () {
return elmContainer;
};
me.getBlockWidth = function () {
return blockWidth;
};
me.getBlockHeight = function () {
return blockHeight;
};
me.setupPlayingField = function () {
if (!elmPlayingField) {
createBoardElements();
}
let cWidth, cHeight;
let cTop, cLeft;
if (config.fullScreen === true) {
cTop = 0;
cLeft = 0;
cWidth = getClientWidth() - 20;
cHeight = getClientHeight() - 20;
} else {
cTop = config.top;
cLeft = config.left;
cWidth = config.width;
cHeight = config.height;
}
const wEdgeSpace =
me.getBlockWidth() * 2 + (cWidth % me.getBlockWidth());
const fWidth = Math.min(
maxBoardWidth() - wEdgeSpace,
cWidth - wEdgeSpace,
);
const hEdgeSpace =
me.getBlockHeight() * 3 + (cHeight % me.getBlockHeight());
const fHeight = Math.min(
maxBoardHeight() - hEdgeSpace,
cHeight - hEdgeSpace,
);
elmContainer.style.left = cLeft + "px";
elmContainer.style.top = cTop + "px";
elmContainer.style.width = cWidth + "px";
elmContainer.style.height = cHeight + "px";
elmPlayingField.style.left = me.getBlockWidth() + "px";
elmPlayingField.style.top = me.getBlockHeight() + "px";
elmPlayingField.style.width = fWidth + "px";
elmPlayingField.style.height = fHeight + "px";
const bottomPanelHeight = hEdgeSpace - me.getBlockHeight();
const pLabelTop =
me.getBlockHeight() +
fHeight +
Math.round((bottomPanelHeight - 30) / 2) +
"px";
elmAboutPanel.style.top = pLabelTop;
elmAboutPanel.style.width = "450px";
elmAboutPanel.style.left =
Math.round(cWidth / 2) - Math.round(450 / 2) + "px";
elmLengthPanel.style.top = pLabelTop;
elmLengthPanel.style.left = 30 + "px";
elmHighscorePanel.style.top = pLabelTop;
elmHighscorePanel.style.left = cWidth - 140 + "px";
if (cWidth < 700) {
elmAboutPanel.style.display = "none";
} else {
elmAboutPanel.style.display = "block";
}
me.grid = [];
const numBoardCols = fWidth / me.getBlockWidth() + 2;
const numBoardRows = fHeight / me.getBlockHeight() + 2;
for (let row = 0; row < numBoardRows; row++) {
me.grid[row] = [];
for (let col = 0; col < numBoardCols; col++) {
if (
col === 0 ||
row === 0 ||
col === numBoardCols - 1 ||
row === numBoardRows - 1
) {
me.grid[row][col] = 1;
} else {
me.grid[row][col] = 0;
}
}
}
myFood.randomlyPlaceFood();
config.onLengthUpdate(1);
myKeyListener = function (evt) {
if (!evt) evt = window.event;
const keyNum = evt.which ? evt.which : evt.keyCode;
if (me.getBoardState() === BOARD_READY) {
if (
!(keyNum >= 37 && keyNum <= 40) &&
!(
keyNum === 87 ||
keyNum === 65 ||
keyNum === 83 ||
keyNum === 68
)
) {
return;
}
SNAKE.removeEventListener(
elmContainer,
"keydown",
myKeyListener,
false,
);
SNAKE.removeEventListener(
elmContainer,
"visibilitychange",
myWindowListener,
false,
);
myKeyListener = function (evt) {
if (!evt) evt = window.event;
const keyNum = evt.which ? evt.which : evt.keyCode;
if (keyNum === 32) {
if (me.getBoardState() != BOARD_NOT_READY)
me.setPaused(!me.getPaused());
}
mySnake.handleArrowKeys(keyNum);
evt.cancelBubble = true;
if (evt.stopPropagation) {
evt.stopPropagation();
}
if (evt.preventDefault) {
evt.preventDefault();
}
return false;
};
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
if (me.getBoardState() != BOARD_NOT_READY && !me.getPaused())
me.setPaused(true);
}
});
SNAKE.addEventListener(
elmContainer,
"keydown",
myKeyListener,
false,
);
SNAKE.addEventListener(
elmContainer,
"visibilitychange",
myWindowListener,
false,
);
mySnake.rebirth();
mySnake.handleArrowKeys(keyNum);
me.setBoardState(BOARD_IN_PLAY);
mySnake.go();
}
evt.cancelBubble = true;
if (evt.stopPropagation) {
evt.stopPropagation();
}
if (evt.preventDefault) {
evt.preventDefault();
}
return false;
};
if (!config.moveSnakeWithAI) {
SNAKE.addEventListener(elmContainer, "keydown", myKeyListener, false);
SNAKE.addEventListener(
elmContainer,
"visibilitychange",
myWindowListener,
false,
);
}
};
me.foodEaten = function () {
config.onLengthUpdate(mySnake.snakeLength);
elmLengthPanel.innerHTML = "Length: " + mySnake.snakeLength;
if (mySnake.snakeLength > localStorage[HIGH_SCORE_KEY]) {
localStorage.setItem(HIGH_SCORE_KEY, mySnake.snakeLength);
elmHighscorePanel.innerHTML =
"Highscore: " + localStorage[HIGH_SCORE_KEY];
}
if (!myFood.randomlyPlaceFood()) {
return false;
}
return true;
};
me.handleDeath = function () {
handleEndCondition(elmTryAgain);
config.onDeath({ startAIGame: me.startAIGame });
};
me.handleWin = function () {
handleEndCondition(elmWin);
config.onWin({ startAIGame: me.startAIGame });
};
me.setSpeed = (speed) => {
mySnake.setSpeed(speed);
};
me.getSpeed = () => {
return mySnake.getSpeed();
};
me.startAIGame = () => {
me.resetBoard();
mySnake.rebirth();
me.setBoardState(BOARD_IN_PLAY);
mySnake.go();
};
config.fullScreen =
typeof config.fullScreen === "undefined" ? false : config.fullScreen;
config.top = typeof config.top === "undefined" ? 0 : config.top;
config.left = typeof config.left === "undefined" ? 0 : config.left;
config.width = typeof config.width === "undefined" ? 400 : config.width;
config.height =
typeof config.height === "undefined" ? 400 : config.height;
config.premoveOnPause =
typeof config.premoveOnPause === "undefined"
? false
: config.premoveOnPause;
if (config.fullScreen) {
SNAKE.addEventListener(
window,
"resize",
function () {
me.setupPlayingField();
},
false,
);
}
me.setBoardState(BOARD_NOT_READY);
if (config.boardContainer) {
me.setBoardContainer(config.boardContainer);
}
const reloadGame = function () {
me.resetBoard();
me.setBoardState(BOARD_READY);
me.getBoardContainer().focus();
};
if (config.onInit) {
config.onInit({
reloadGame,
getSpeed: me.getSpeed,
setSpeed: me.setSpeed,
startAIGame: me.startAIGame,
});
}
};
})();