Last active
January 30, 2024 21:13
-
-
Save seliverstov-maxim/468b4ef3ead770722d335bdb316eac8a to your computer and use it in GitHub Desktop.
snake js for begginers in one file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<body> | |
</body> | |
<style> | |
table { background-color: #b7bea1; } | |
td { width: 10px; height: 10px; border: solid 1px #707462; } | |
</style> | |
<script> | |
// DISPLAY MODULE BEGIN | |
const append = function(name, attrs) { | |
const tmp = document.createElement(name); | |
if (attrs.id) { tmp.setAttribute('id', attrs.id) } | |
document.querySelector(attrs.to).appendChild(tmp); | |
return tmp; | |
} | |
const initDisplay = function() { | |
append('table', {to: 'body'}); | |
append('tbody', {to: 'table'}); | |
for (let y = 0; y < MAX_Y; y++) { | |
append('tr', {id: `row_${y}`, to: 'tbody'}); | |
for (let x = 0; x < MAX_X; x++) { | |
append('td', {id: `cell_${x}_${y}`, to: `#row_${y}`}); | |
} | |
} | |
} | |
const markAsFilled = function(tag) { tag.style.backgroundColor = '#000'; } | |
const markAsEmpty = function(tag) { tag.style.backgroundColor = '#bbc0a9'; } | |
const printDisplay = function (field) { | |
for (let y = 0; y < MAX_Y; y++) { | |
for (let x = 0; x < MAX_X; x++) { | |
const td = document.querySelector(`#cell_${x}_${y}`) | |
switch(field[x][y]) { | |
case FIELD_SNAKE_VALUE: | |
case FIELD_APPLE_VALUE: markAsFilled(td); break; | |
case FIELD_EMPTY_VALUE: markAsEmpty(td); break; | |
} | |
} | |
} | |
return true; | |
} | |
// DISPLAY MODULE END | |
// GAME CORE BEGIN | |
const pushToSnake = function(snake, x, y, field) { | |
const tmp = {x: x, y: y} | |
snake.push(tmp); | |
field[tmp.x][tmp.y] = FIELD_SNAKE_VALUE | |
} | |
const shiftSnake = function(snake, x, y, field) { | |
snake.shift(); | |
field[x][y] = FIELD_EMPTY_VALUE | |
} | |
const nextCoords = function(coords, direction) { | |
if (direction === LEFT_DIRECTION) { return wrapCycledField({x: coords.x - 1, y: coords.y}) } | |
if (direction === RIGHT_DIRECTION) { return wrapCycledField({x: coords.x + 1, y: coords.y}) } | |
if (direction === UP_DIRECTION) { return wrapCycledField({x: coords.x, y: coords.y - 1}) } | |
if (direction === DOWN_DIRECTION) { return wrapCycledField({x: coords.x, y: coords.y + 1}) } | |
} | |
const wrapCycledField = function(coords) { | |
if (coords.x >= MAX_X) { return {x: 0, y: coords.y} } | |
if (coords.y >= MAX_Y) { return {x: coords.x, y: 0} } | |
if (coords.x < 0) { return {x: MAX_X-1, y: coords.y} } | |
if (coords.y < 0) { return {x: coords.x, y: MAX_Y - 1} } | |
return coords; | |
} | |
const addNewApple = function(field) { | |
let x, y; | |
do { | |
x = Math.round(Math.random() * (MAX_X - 1)); | |
y = Math.round(Math.random() * (MAX_Y - 1)); | |
} while(field[x][y] !== FIELD_EMPTY_VALUE) | |
return field[x][y] = FIELD_APPLE_VALUE; | |
} | |
const eatAppleIfYouCan = function(snake, direction, field) { | |
const head = snake[snake.length - 1]; | |
const target = nextCoords(head, direction); | |
if (!field[target.x]) { return } | |
if(field[target.x][target.y] === FIELD_APPLE_VALUE) { | |
pushToSnake(snake, target.x, target.y, field); | |
addNewApple(field); | |
} | |
} | |
const moveSnake = function(snake, direction, field) { | |
eatAppleIfYouCan(snake, direction, field) | |
const head = snake[snake.length - 1]; | |
const tail = snake[0]; | |
const nextStepCoords = nextCoords(head, direction); | |
switch(field[nextStepCoords.x][nextStepCoords.y]) { | |
case FIELD_APPLE_VALUE: | |
pushToSnake(snake, nextStepCoords.x, nextStepCoords.y, field); | |
shiftSnake(snake, tail.x, tail.y, field); | |
break; | |
case FIELD_EMPTY_VALUE: | |
pushToSnake(snake, nextStepCoords.x, nextStepCoords.y, field); | |
shiftSnake(snake, tail.x, tail.y, field); | |
break; | |
case FIELD_SNAKE_VALUE: | |
return GAME_OVER; | |
} | |
return GAME_NEXT; | |
} | |
const gameTick = function(field, snake, direction) { // ENTRANCE TO LOGIC, USED IN WRAPPER | |
const status = moveSnake(snake, direction, field) | |
if (status !== GAME_NEXT) { return status } | |
printDisplay(field); | |
} | |
// GAME CORE END | |
// GAME WRAPPER AND CONTROLLER BEGIN | |
const canSwitchDirection = function(futureDirection) { | |
if (!futureDirection) { return } | |
if ([DOWN_DIRECTION, UP_DIRECTION].indexOf(direction) !== -1 && [DOWN_DIRECTION, UP_DIRECTION].indexOf(futureDirection) !== -1) { return } | |
if ([LEFT_DIRECTION, RIGHT_DIRECTION].indexOf(direction) !== -1 && [LEFT_DIRECTION, RIGHT_DIRECTION].indexOf(futureDirection) !== -1) { return } | |
return true | |
} | |
const gameWrapper = function() { // TO MANAGE LEVEL UP, GAME OVER | |
if (canSwitchDirection(futureDirection)) { direction = futureDirection } | |
const status = gameTick(field, snake, direction); | |
if (status === GAME_OVER) { | |
clearInterval(intervalId); | |
alert('game is over'); | |
window.location.reload(); | |
return; | |
} | |
if (status === GAME_LEVEL_UP) { | |
tickTime -= SPEED_UP_FACTOR; | |
clearInterval(intervalId); | |
intervalId = setInterval(gemWrapper, tickTime); | |
} | |
} | |
const matchDirection = function(e) { | |
switch(e.key) { | |
case 'ArrowUp': return UP_DIRECTION; | |
case 'ArrowDown': return DOWN_DIRECTION; | |
case 'ArrowLeft': return LEFT_DIRECTION; | |
case 'ArrowRight': return RIGHT_DIRECTION; | |
} | |
} | |
document.addEventListener('keyup', function(e) { | |
e.preventDefault(); | |
tmp = matchDirection(e); | |
if(tmp) { futureDirection = tmp } | |
}) | |
// GAME WRAPPER AND CONTROLLER END | |
const MAX_Y = 10; | |
const MAX_X = 10; | |
const FIELD_EMPTY_VALUE = 0; | |
const FIELD_SNAKE_VALUE = 1; | |
const FIELD_APPLE_VALUE = 2; | |
const LEFT_DIRECTION = 'left'; | |
const RIGHT_DIRECTION = 'right'; | |
const UP_DIRECTION = 'up'; | |
const DOWN_DIRECTION = 'down'; | |
const GAME_NEXT = 0; | |
const GAME_OVER = 1; | |
const GAME_LEVEL_UP = 2; | |
const SPEED_UP_FACTOR = 75; | |
const snake = Array(); | |
const field = Array(MAX_X).fill().map(() => Array(MAX_Y).fill(0)); | |
const startAt = {x: 0, y: 1} | |
let direction = RIGHT_DIRECTION; // can be left, right, up, down | |
let futureDirection = direction; | |
let tickTime = 500; // in milisec. | |
pushToSnake(snake, startAt.x, startAt.y, field); | |
addNewApple(field); | |
initDisplay(field); | |
printDisplay(field); | |
let intervalId = setInterval(gameWrapper, tickTime); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Snake game based on table markup graphic and pure js, primary old syntax - very simple to start.