Created
May 30, 2017 17:39
-
-
Save anonymous/0d2e13215bb80db2a02047d8f31e1340 to your computer and use it in GitHub Desktop.
SNAKE // source http://jsbin.com/kukajejuzi
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> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>SNAKE</title> | |
<style id="jsbin-css"> | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
html { | |
font-family: 'segoe ui'; | |
font-weight: 100; | |
font-size: 10vw; | |
line-height: 10vw; | |
background: white; | |
color: black; | |
height: 100%; | |
width: 100%; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
min-height: 100%; | |
} | |
section { | |
font-size: 0.5rem; | |
line-height: 0.5rem; | |
} | |
canvas { | |
margin: 0.5rem 0; | |
width: 100%; | |
box-shadow: 0 0.25rem 0.75rem #f7f7f7; | |
} | |
main { | |
width: 90%; | |
} | |
</style> | |
</head> | |
<body> | |
<main> | |
SNAKE | |
<section> | |
Score: <span id='score'>0</span> | |
<br> | |
Highscore: <span id='highscore'>0</span> | |
</section> | |
<canvas width='512' height='512'></canvas> | |
</main> | |
<script id="jsbin-javascript"> | |
let next_apple = environment => { | |
return { | |
x: Math.floor(Math.random() * environment.tiles), | |
y: Math.floor(Math.random() * environment.tiles) | |
} | |
}; | |
let init = (highscore, environment) => { | |
return { | |
apple: next_apple(environment), | |
player: { | |
pos: { | |
x: Math.floor(environment.tiles / 2), | |
y: Math.floor(environment.tiles / 2) | |
}, | |
speed: { | |
dx: 0, | |
dy: 0 | |
} | |
}, | |
tail: { | |
links: [], | |
length: 5 | |
}, | |
score: 0, | |
highscore: highscore | |
} | |
}; | |
let next_speed = (speed, key) => { | |
if(key.left || key.up || key.right || key.down) { | |
return { | |
dx: (key.right ? 1 : 0) - (key.left ? 1 : 0), | |
dy: (key.down ? 1 : 0) - (key.up ? 1 : 0) | |
} | |
} else { | |
return { | |
dx: speed.dx, | |
dy: speed.dy | |
} | |
} | |
}; | |
let is_moving = (speed) => speed.dx !== 0 || speed.dy !== 0; | |
let move_player = (player, environment) => { | |
let speed = next_speed(player.speed, environment.key); | |
return { | |
pos: { | |
x: (player.pos.x + speed.dx + environment.tiles) % environment.tiles, | |
y: (player.pos.y + speed.dy + environment.tiles) % environment.tiles | |
}, | |
speed: { | |
dx: speed.dx, | |
dy: speed.dy | |
} | |
} | |
}; | |
let is_apple_eating = (pos, apple) => pos.x === apple.x && pos.y === apple.y; | |
let bites_link = (pos, link) => link.x === pos.x && link.y === pos.y; | |
let bites_links = (pos, links) => links.find(link => bites_link(pos, link)) !== undefined; | |
let bites_himself = (player, tail) => is_moving(player.speed) && bites_links(player.pos, tail.links); | |
let next_tail = (tail, pos, is_growing) => { | |
if(is_growing === true) { | |
return { | |
links: tail.links.concat(pos), | |
length: tail.length + 1 | |
}; | |
} else { | |
return { | |
links: tail.links.concat(pos).slice(-tail.length), | |
length: tail.length | |
}; | |
} | |
}; | |
let update = (state, environment) => { | |
let player = move_player(state.player, environment); | |
let eaten = is_apple_eating(player.pos, state.apple); | |
let bitten = bites_himself(player, state.tail); | |
if(bitten === true) { | |
return init(state.highscore, environment); | |
} else if(eaten === true) { | |
let score = state.score + 1; | |
let highscore = score > state.highscore ? score : state.highscore; | |
return { | |
apple: next_apple(environment), | |
player: player, | |
tail: next_tail(state.tail, player.pos, true), | |
score: score, | |
highscore: highscore | |
}; | |
} else { | |
return { | |
apple: state.apple, | |
player: player, | |
tail: next_tail(state.tail, player.pos, false), | |
score: state.score, | |
highscore: state.highscore | |
}; | |
} | |
}; | |
let draw = (state, ui, environment) => { | |
let gridSize = ui.width / environment.tiles; | |
let c = ui.context; | |
c.fillStyle = '#EEE'; | |
c.fillRect(0, 0, ui.width, ui.height); | |
state.tail.links.forEach((tailbit, index) => { | |
var scale = 0.75 + 0.25 * index / state.tail.length; | |
c.fillStyle = 'darkgreen'; | |
c.fillRect( | |
tailbit.x * gridSize + (gridSize - ((gridSize - 2) * scale)) / 2, | |
tailbit.y * gridSize + (gridSize - ((gridSize - 2) * scale)) / 2, | |
(gridSize - 2) * scale, | |
(gridSize - 2) * scale | |
); | |
}); | |
c.fillStyle = 'green'; | |
c.fillRect( | |
state.player.pos.x * gridSize + 1, | |
state.player.pos.y * gridSize + 1, | |
gridSize - 2, | |
gridSize - 2 | |
); | |
c.fillStyle = 'red'; | |
c.fillRect( | |
state.apple.x * gridSize + 1, | |
state.apple.y * gridSize + 1, | |
gridSize - 2, | |
gridSize - 2 | |
); | |
ui.score.innerText = state.score; | |
ui.highscore.innerText = state.highscore; | |
}; | |
let update_environment = (environment, keys) => { // the one function that modifies stuff | |
console.clear(); | |
let key = keys.pop(); | |
while(keys.length > 0) { | |
keys.pop(); | |
} | |
return { | |
tiles: environment.tiles, | |
key: { | |
left: key === 0, | |
up: key === 1, | |
right: key === 2, | |
down: key === 3 | |
} | |
} | |
} | |
window.onload = () => { | |
let canvas = document.querySelector('canvas'); | |
let keys = []; | |
document.addEventListener('keydown', event => keys.push(event.keyCode - 37)); | |
var environment = { | |
tiles: 16, | |
key: undefined | |
}; | |
let ui = { | |
context: canvas.getContext('2d'), | |
width: canvas.width, | |
height: canvas.height, | |
score: document.getElementById('score'), | |
highscore: document.getElementById('highscore') | |
}; | |
var state = init(0, environment); | |
setInterval(() => { | |
environment = update_environment(environment, keys); | |
state = update(state, environment); | |
draw(state, ui, environment); | |
}, 1000/15); | |
}; | |
</script> | |
</body> | |
</html> |
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
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
html { | |
font-family: 'segoe ui'; | |
font-weight: 100; | |
font-size: 10vw; | |
line-height: 10vw; | |
background: white; | |
color: black; | |
height: 100%; | |
width: 100%; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
min-height: 100%; | |
} | |
section { | |
font-size: 0.5rem; | |
line-height: 0.5rem; | |
} | |
canvas { | |
margin: 0.5rem 0; | |
width: 100%; | |
box-shadow: 0 0.25rem 0.75rem #f7f7f7; | |
} | |
main { | |
width: 90%; | |
} |
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
let next_apple = environment => { | |
return { | |
x: Math.floor(Math.random() * environment.tiles), | |
y: Math.floor(Math.random() * environment.tiles) | |
} | |
}; | |
let init = (highscore, environment) => { | |
return { | |
apple: next_apple(environment), | |
player: { | |
pos: { | |
x: Math.floor(environment.tiles / 2), | |
y: Math.floor(environment.tiles / 2) | |
}, | |
speed: { | |
dx: 0, | |
dy: 0 | |
} | |
}, | |
tail: { | |
links: [], | |
length: 5 | |
}, | |
score: 0, | |
highscore: highscore | |
} | |
}; | |
let next_speed = (speed, key) => { | |
if(key.left || key.up || key.right || key.down) { | |
return { | |
dx: (key.right ? 1 : 0) - (key.left ? 1 : 0), | |
dy: (key.down ? 1 : 0) - (key.up ? 1 : 0) | |
} | |
} else { | |
return { | |
dx: speed.dx, | |
dy: speed.dy | |
} | |
} | |
}; | |
let is_moving = (speed) => speed.dx !== 0 || speed.dy !== 0; | |
let move_player = (player, environment) => { | |
let speed = next_speed(player.speed, environment.key); | |
return { | |
pos: { | |
x: (player.pos.x + speed.dx + environment.tiles) % environment.tiles, | |
y: (player.pos.y + speed.dy + environment.tiles) % environment.tiles | |
}, | |
speed: { | |
dx: speed.dx, | |
dy: speed.dy | |
} | |
} | |
}; | |
let is_apple_eating = (pos, apple) => pos.x === apple.x && pos.y === apple.y; | |
let bites_link = (pos, link) => link.x === pos.x && link.y === pos.y; | |
let bites_links = (pos, links) => links.find(link => bites_link(pos, link)) !== undefined; | |
let bites_himself = (player, tail) => is_moving(player.speed) && bites_links(player.pos, tail.links); | |
let next_tail = (tail, pos, is_growing) => { | |
if(is_growing === true) { | |
return { | |
links: tail.links.concat(pos), | |
length: tail.length + 1 | |
}; | |
} else { | |
return { | |
links: tail.links.concat(pos).slice(-tail.length), | |
length: tail.length | |
}; | |
} | |
}; | |
let update = (state, environment) => { | |
let player = move_player(state.player, environment); | |
let eaten = is_apple_eating(player.pos, state.apple); | |
let bitten = bites_himself(player, state.tail); | |
if(bitten === true) { | |
return init(state.highscore, environment); | |
} else if(eaten === true) { | |
let score = state.score + 1; | |
let highscore = score > state.highscore ? score : state.highscore; | |
return { | |
apple: next_apple(environment), | |
player: player, | |
tail: next_tail(state.tail, player.pos, true), | |
score: score, | |
highscore: highscore | |
}; | |
} else { | |
return { | |
apple: state.apple, | |
player: player, | |
tail: next_tail(state.tail, player.pos, false), | |
score: state.score, | |
highscore: state.highscore | |
}; | |
} | |
}; | |
let draw = (state, ui, environment) => { | |
let gridSize = ui.width / environment.tiles; | |
let c = ui.context; | |
c.fillStyle = '#EEE'; | |
c.fillRect(0, 0, ui.width, ui.height); | |
state.tail.links.forEach((tailbit, index) => { | |
var scale = 0.75 + 0.25 * index / state.tail.length; | |
c.fillStyle = 'darkgreen'; | |
c.fillRect( | |
tailbit.x * gridSize + (gridSize - ((gridSize - 2) * scale)) / 2, | |
tailbit.y * gridSize + (gridSize - ((gridSize - 2) * scale)) / 2, | |
(gridSize - 2) * scale, | |
(gridSize - 2) * scale | |
); | |
}); | |
c.fillStyle = 'green'; | |
c.fillRect( | |
state.player.pos.x * gridSize + 1, | |
state.player.pos.y * gridSize + 1, | |
gridSize - 2, | |
gridSize - 2 | |
); | |
c.fillStyle = 'red'; | |
c.fillRect( | |
state.apple.x * gridSize + 1, | |
state.apple.y * gridSize + 1, | |
gridSize - 2, | |
gridSize - 2 | |
); | |
ui.score.innerText = state.score; | |
ui.highscore.innerText = state.highscore; | |
}; | |
let update_environment = (environment, keys) => { // the one function that modifies stuff | |
console.clear(); | |
let key = keys.pop(); | |
while(keys.length > 0) { | |
keys.pop(); | |
} | |
return { | |
tiles: environment.tiles, | |
key: { | |
left: key === 0, | |
up: key === 1, | |
right: key === 2, | |
down: key === 3 | |
} | |
} | |
} | |
window.onload = () => { | |
let canvas = document.querySelector('canvas'); | |
let keys = []; | |
document.addEventListener('keydown', event => keys.push(event.keyCode - 37)); | |
var environment = { | |
tiles: 16, | |
key: undefined | |
}; | |
let ui = { | |
context: canvas.getContext('2d'), | |
width: canvas.width, | |
height: canvas.height, | |
score: document.getElementById('score'), | |
highscore: document.getElementById('highscore') | |
}; | |
var state = init(0, environment); | |
setInterval(() => { | |
environment = update_environment(environment, keys); | |
state = update(state, environment); | |
draw(state, ui, environment); | |
}, 1000/15); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment