Skip to content

Instantly share code, notes, and snippets.

@tripl3dogdare
Last active July 16, 2018 17:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tripl3dogdare/4cad289270c5a31abade4f78e3f90f50 to your computer and use it in GitHub Desktop.
Save tripl3dogdare/4cad289270c5a31abade4f78e3f90f50 to your computer and use it in GitHub Desktop.
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))), []))
<!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