Last active
July 16, 2018 17:55
-
-
Save tripl3dogdare/4cad289270c5a31abade4f78e3f90f50 to your computer and use it in GitHub Desktop.
This file contains 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
var canvas, board, score | |
function initGame() { | |
let canvasElem = document.getElementById("game") | |
canvas = canvasElem.getContext('2d') | |
canvasElem.setAttribute('height', 640) | |
canvasElem.setAttribute('width', 640) | |
setInterval(() => { | |
canvasElem.style.height = Math.min(Math.max(window.innerWidth/1.5, 260), Math.max(window.innerHeight/1.5, 360)) + "px" | |
canvasElem.style.width = canvasElem.style.height | |
}, 5) | |
resetGame() | |
} | |
function resetGame() { | |
board = {} // TEST_BOARD | |
score = 0 | |
spawnTile() | |
spawnTile() | |
draw() | |
document.onkeydown = handleKeypress | |
} | |
function draw() { | |
canvas.clearRect(0,0,640,640) | |
canvas.textAlign = "center" | |
canvas.textBaseline = "middle" | |
cartesianProduct([[0,1,2,3],[0,1,2,3]]).forEach(([x,y]) => { | |
let tile = board[pos(x,y)], drawX = x*163, drawY = y*163 | |
if(tile) { | |
canvas.fillStyle = tile < 4096 ? COLORS[tile] : COLORS.big | |
drawRoundRect(drawX, drawY) | |
canvas.fillStyle = tile > 4 ? COLORS.lightText : COLORS.darkText | |
canvas.font = `bold ${50-5*((tile+'').length-3)}px sans-serif` | |
canvas.fillText(tile, drawX+75, drawY+75) | |
} else { | |
canvas.fillStyle = COLORS.empty | |
drawRoundRect(drawX, drawY) | |
} | |
}) | |
document.querySelector(".score#curr span").innerText = score | |
document.querySelector(".score#high span").innerText = Math.max(score, localStorage.highScore || 0) | |
} | |
function drawRoundRect(x, y, w=150, h=150, r=10) { | |
canvas.beginPath() | |
canvas.moveTo(x + r, y); | |
canvas.lineTo(x + w - r, y); | |
canvas.quadraticCurveTo(x + w, y, x + w, y + r); | |
canvas.lineTo(x + w, y + h - r); | |
canvas.quadraticCurveTo(x + w, y + h, x + w - r, y + h); | |
canvas.lineTo(x + r, y + h); | |
canvas.quadraticCurveTo(x, y + h, x, y + h - r); | |
canvas.lineTo(x, y + r); | |
canvas.quadraticCurveTo(x, y, x + r, y); | |
canvas.closePath() | |
canvas.fill() | |
} | |
function drawGameOver() { | |
canvas.fillStyle = 'rgba(0,0,0,0.5)' | |
drawRoundRect(0,0,640,640) | |
canvas.fillStyle = 'white' | |
canvas.font = "64px sans-serif" | |
canvas.fillText("Game Over", 320, 280) | |
canvas.font = "30px sans-serif" | |
canvas.fillText("Press SPACE to play again", 320, 340) | |
} | |
function spawnTile() { | |
if(Object.keys(board).length > 15) return | |
let x, y | |
do { | |
x = Math.floor(Math.random()*4) | |
y = Math.floor(Math.random()*4) | |
} while(board[pos(x,y)]) | |
board[pos(x,y)] = | |
Math.floor(Math.random()*10) ? 2 : 4 | |
} | |
function handleKeypress({key}) { | |
if(!board) return | |
document.onkeydown = undefined | |
let oldBoard = Object.assign({}, board), isValid = true | |
switch(key) { | |
case "ArrowUp": case "w": moveUp(); break | |
case "ArrowRight": case "d": moveRight(); break | |
case "ArrowDown": case "s": moveDown(); break | |
case "ArrowLeft": case "a": moveLeft(); break | |
default: isValid = false | |
} | |
if(isValid && oldBoard != board) { | |
Object.keys(board).map(k => {if(board[k].newValue) { board[k] = board[k].newValue; score += board[k] }}) | |
localStorage.highScore = Math.max(score, localStorage.highScore ? localStorage.highScore : 0) | |
spawnTile() | |
draw() | |
if(Object.keys(board).length > 15) { | |
let gameOver = true | |
cartesianProduct([[0,1,2,3],[0,1,2,3]]).forEach(([x,y]) => | |
gameOver = gameOver && | |
board[pos(x,y)] != board[pos(x+1,y)] && | |
board[pos(x,y)] != board[pos(x-1,y)] && | |
board[pos(x,y)] != board[pos(x,y+1)] && | |
board[pos(x,y)] != board[pos(x,y-1)]) | |
if(gameOver) | |
return drawGameOver(document.onkeydown = (({key}) => key == " " ? resetGame() : null)) | |
} | |
} else draw() | |
document.onkeydown = handleKeypress | |
} | |
const moveUp = () => | |
cartesianProduct([[0,1,2,3],[1,2,3]]).forEach(([x,y]) => { | |
let tile = board[pos(x,y)], into | |
if(!tile) return | |
delete board[pos(x,y)] | |
while(!(into = board[pos(x,y-1)]) && y > 0) y-- | |
if(into == tile) { y--; tile = {newValue:tile*2} } | |
board[pos(x,y)] = tile | |
}) | |
const moveRight = () => | |
cartesianProduct([[2,1,0],[0,1,2,3]]).forEach(([x,y]) => { | |
let tile = board[pos(x,y)], into | |
if(!tile) return | |
delete board[pos(x,y)] | |
while(!(into = board[pos(x+1,y)]) && x < 3) x++ | |
if(into == tile) { x++; tile = {newValue:tile*2} } | |
board[pos(x,y)] = tile | |
}) | |
const moveDown = () => | |
cartesianProduct([[0,1,2,3],[2,1,0]]).forEach(([x,y]) => { | |
let tile = board[pos(x,y)], into | |
if(!tile) return | |
delete board[pos(x,y)] | |
while(!(into = board[pos(x,y+1)]) && y < 3) y++ | |
if(into == tile) { y++; tile = {newValue:tile*2} } | |
board[pos(x,y)] = tile | |
}) | |
const moveLeft = () => | |
cartesianProduct([[1,2,3],[0,1,2,3]]).forEach(([x,y]) => { | |
let tile = board[pos(x,y)], into | |
if(!tile) return | |
delete board[pos(x,y)] | |
while(!(into = board[pos(x-1,y)]) && x > 0) x-- | |
if(into == tile) { x--; tile = {newValue:tile*2} } | |
board[pos(x,y)] = tile | |
}) | |
const COLORS = { | |
2: "#ECE2D8", | |
4: "#DED3BF", | |
8: "#F1AE7A", | |
16: "#F79264", | |
32: "#F67C5F", | |
64: "#F95A3D", | |
128: "#E4452F", | |
256: "#CB6501", | |
512: "#FE9730", | |
1024: "#EDC73A", | |
2048: "#F3C529", | |
"big": "#3D3A33", | |
"empty": "#CBBFB3", | |
"lightText": "#F7FFF6", | |
"darkText": "#7A6D64" | |
} | |
const TEST_BOARD = { | |
11: 2, 21: 32, 31: 512, 41: 8192, | |
12: 4, 22: 64, 32: 1024, 42: 16384, | |
13: 8, 23: 128, 33: 2048, 43: 32768, | |
14: 16, 24: 256, 34: 4096, 44: 65536 | |
} | |
const pos = (x,y) => (x+1)*10+y+1 | |
const cartesianProduct = (arr) => | |
arr.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), [])) |
This file contains 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> | |
<title>2048</title> | |
<meta charset="utf8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
* { padding: 0; margin: 0; box-sizing: border-box; font-family: sans-serif; } | |
body { padding: 10px; width: 100%; height: 100vh; background: #B8AC9E; } | |
#wrapper { display: table; margin: 10px auto; font-size: 14px; } | |
#header { display: flex; flex-flow: column wrap; max-height: 6rem; } | |
h1 { font-size: 3rem; } | |
#game { margin: 10px 0; } | |
.score, a[onclick] { padding: 5px; margin-bottom: 5px; background-color: #CBBFB3; border-radius: 10px; } | |
a[onclick] { cursor: pointer; } | |
</style> | |
</head> | |
<body> | |
<div id=wrapper> | |
<div id=header> | |
<h1>2048</h1> | |
<p>A shameless clone created by Connor Scialdone.</p> | |
<a href="https://gist.github.com/tripl3dogdare/4cad289270c5a31abade4f78e3f90f50">See the code on Github</a> | |
<p class=score id=curr>Score: <span></span></p> | |
<p class=score id=high>High Score: <span></span></p> | |
<a onclick="window.location = window.location">Reset</a> | |
</div> | |
<canvas id="game">Whoops! Looks like your browser doesn't support the canvas element. Sorry...</canvas> | |
<p>Use the arrow keys or WASD to move.</p> | |
<p>Combine tiles to double to 2048 before you run out of space.</p> | |
</div> | |
</body> | |
<script src=game.js onload="initGame()"></script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment