<!DOCTYPE html>
<html lang="en">
<head>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
<script type="application/javascript">
let img;
function Tile(pos, val, puzzle){
this.pos = pos;
this.val = val;
this.puzzle = puzzle;
this.merging = false;
this.getCol = () => Math.round(this.pos % 4);
this.getRow = () => Math.floor(this.pos / 4);
this.show = function() {
let size = 0.25*width;
noStroke();
image(img[this.val],(this.getCol())*size, (this.getRow())*size);
};
this.move = function(dir) {
let col = this.getCol() + (1 - 2*(dir < 0))*Math.abs(dir)%4;
let row = this.getRow() + (1 - 2*(dir < 0))*Math.floor(Math.abs(dir)/4);
let target = this.puzzle.getTile(this.pos + dir);
if (col < 0 || col > 3 || row < 0 || row > 3) {
return false;
} else if (target) {
if(this.merging || target.merging || target.val !== this.val)
return false;
target.val += this.val;
target.merging = true;
this.puzzle.score += target.val;
this.puzzle.removeTile(this);
return true;
}
this.pos += dir;
return true;
}
}
function Puzzle(){
this.tiles = [];
this.dir = 0;
this.score = 0;
this.hasMoved = false;
this.validPositions = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
this.getOpenPositions = () => this.validPositions.filter(i => this.tiles.map(x => x.pos).indexOf(i) === -1);
this.getTile = pos => this.tiles.filter(x => x.pos === pos)[0];
this.removeTile = tile => this.tiles.splice(this.tiles.indexOf(tile), 1);
this.validMoves = function(){
if(this.tiles.length < 16)
return true;
let res = false;
this.tiles.sort((x,y) => x.pos - y.pos);
for(let i = 0; i < 16; i++)
res = res || ( (i%4 < 3) ? this.tiles[i].val === this.tiles[i+1].val : false )
|| ( (i < 12) ? this.tiles[i].val === this.tiles[i+4].val : false );
return res;
};
this.checkGameState = function(){
if (!this.validMoves()){
alert('Game Over!');
this.restart();
}
};
this.restart = function(){
this.tiles = [];
this.dir = 0;
this.score = 0;
this.hasMoved = false;
this.generateTile();
this.generateTile();
};
this.show = function(){
background([251,244,252]);
fill([248,214,247]);
textSize(0.05*width);
textAlign(CENTER, TOP);
text("SCORE: " + this.score, 0.5*width, width);
for(let tile of this.tiles)
tile.show();
};
this.animate = function(){
if(this.dir === 0)
return;
let moving = false;
this.tiles.sort((x,y) => this.dir*(y.pos - x.pos));
for(let tile of this.tiles)
moving = moving || tile.move(this.dir);
if(this.hasMoved && !moving){
this.dir = 0;
this.generateTile();
for(let tile of this.tiles)
tile.merging = false;
}
this.hasMoved = moving;
};
this.generateTile = function(){
let positions = this.getOpenPositions();
let pos = positions[Math.floor(Math.random()*positions.length)];
let val = 2 + 2*Math.floor(Math.random()*1.11);
this.tiles.push(new Tile(pos, val, this));
};
this.generateTile();
this.generateTile();
this.keyHandler = function(key) {
if (key === UP_ARROW) this.dir = -4;
else if (key === DOWN_ARROW) this.dir = 4;
else if (key === RIGHT_ARROW) this.dir = 1;
else if (key === LEFT_ARROW) this.dir = -1;
}
}
let game;
function setup() {
createCanvas(400, 420);
img = {
2:loadImage('2.png'),
4:loadImage('4.png'),
8:loadImage('8.png'),
16:loadImage('16.png'),
32:loadImage('32.png'),
64:loadImage('64.png'),
128:loadImage('128.png'),
256:loadImage('256.png'),
512:loadImage('512.png'),
1024:loadImage('1024.png'),
2048:loadImage('2048.png'),
4096:loadImage('4096.png'),
8192:loadImage('8192.png'),
16384:loadImage('16384.png'),
32768:loadImage('32768.png'),
65536:loadImage('65536.png')
};
game = new Puzzle();
}
function draw() {
game.checkGameState();
game.animate();
game.show();
}
function keyPressed(){
game.keyHandler(keyCode);
}
let preX = 0;
let preY = 0;
function touchStarted(event) {
preX = mouseX;
preY = mouseY;
if (touches.length === 1) event.preventDefault();
}
function touchEnded() {
let dx = mouseX - preX;
let dy = mouseY - preY;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 0) game.keyHandler(RIGHT_ARROW);
else game.keyHandler(LEFT_ARROW);
} else {
if (dy < 0) game.keyHandler(UP_ARROW);
else game.keyHandler(DOWN_ARROW);
}
}
</script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>2048</title>
</head>
<body id="main" onload="(() => $('#main').css('transform','scale(' + (window.innerHeight < window.innerWidth ? window.innerHeight : window.innerWidth) / 420 + ')'))()" style="position: absolute; transform-origin: 0 0"></body>
</html>