var Tile = Container.expand(function (value) {
	var self = Container.call(this);
	self.alpha = 0;
	var fadeIn = function () {
		if (self.alpha < 1) {
			self.alpha = Math.min(self.alpha + 0.24, 1);
			self.scale.set(self.alpha);
			LK.setTimeout(fadeIn, 16.67);
		} else {
			self.alpha = 1;
			self.scale.set(1);
		}
	};
	fadeIn();
	self.value = value || 2;
	var tileGraphics = self.createAsset('tile', 'Tile Graphics', .5, .5);
	var valueToColor = function (value) {
		var colors = [0xFFFFFF, 0xFFE5E5, 0xFFCCCC, 0xFFB2B2, 0xFF9999, 0xFF7F7F, 0xFF6666, 0xFF4C4C, 0xFF3232, 0xFF1919, 0xFF0000];
		var index = Math.min(Math.log2(value) - 1, colors.length - 1);
		return colors[index];
	};
	var tileLabel = new Text2(self.value.toString(), {
		size: 300,
		fill: '#332d28',
		font: 'bold'
	});
	tileLabel.anchor.set(0.5, 0.5);
	tileLabel.x = 0;
	tileLabel.y = -40;
	self.addChild(tileLabel);
	self.setValue = function (value) {
		self.newValue = value;
		tileGraphics.tint = valueToColor(value);
	};
	self.updateToRealValue = function () {
		if (self.newValue) {
			self.value = self.newValue;
			tileLabel.setText(self.value.toString());
			var textScale = Math.min(1, 350 / (tileLabel.width / tileLabel.scale.x));
			tileLabel.scale.set(textScale);
			self.newValue = null;
			self.scale.set(1.2);
			var scaleDown = function () {
				if (self.scale.x > 1) {
					self.scale.x -= 0.02;
					self.scale.y -= 0.02;
					LK.setTimeout(scaleDown, 16.67);
				} else {
					self.scale.set(1);
				}
			};
			scaleDown();
		}
	};
	self.targetX = 0;
	self.targetY = 0;
	self.isMoving = false;
	self.merged = false;
	self.setPosition = function (x, y, instant) {
		self.targetX = x * 500 + 250;
		self.targetY = y * 500 + 250;
		if (instant) {
			self.x = self.targetX;
			self.y = self.targetY;
		}
	};
	self.move = function () {
		var dx = self.targetX - self.x;
		var dy = self.targetY - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 150) {
			self.isMoving = true;
			self.x += dx / distance * 150;
			self.y += dy / distance * 150;
			return false;
		}
		self.isMoving = false;
		self.x = self.targetX;
		self.y = self.targetY;
		return true;
	};
});
var Game = Container.expand(function () {
	var self = Container.call(this);
	self.canMove = function () {
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] === null) {
					return true;
				}
				if (j !== 0 && squares[i][j].value === squares[i][j - 1].value) {
					return true;
				}
				if (i !== 0 && squares[i][j].value === squares[i - 1][j].value) {
					return true;
				}
			}
		}
		return false;
	};
	var scoreTxt = new Text2('2', {
		size: 220,
		fill: '#000000',
		font: 'Impact'
	});
	scoreTxt.anchor.set(.5, 0);
	LK.gui.topCenter.addChild(scoreTxt);
	var tutorialTxt = new Text2('Swipe to merge & merge', {
		size: 120,
		fill: '#000000',
		font: 'Impact'
	});
	tutorialTxt.anchor.set(.5, 1);
	LK.gui.bottom.addChild(tutorialTxt);
	tutorialTxt.y -= 30;
	var toBeRemoved = [];
	self.addRandomTile = function () {
		var emptyPositions = [];
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] === null) {
					emptyPositions.push({
						x: i,
						y: j
					});
				}
			}
		}
		if (emptyPositions.length > 0) {
			var randomPosition = emptyPositions[Math.floor(Math.random() * emptyPositions.length)];
			var square = new Tile(Math.random() < 0.5 ? 2 : 4);
			square.setPosition(randomPosition.x, randomPosition.y, true);
			squares[randomPosition.x][randomPosition.y] = square;
			gridContainer.addChild(square);
		}
	};
	self.mergeTiles = function (x1, y1, x2, y2) {
		if (squares[x1][y1] !== null && squares[x2][y2] !== null && squares[x1][y1].value === squares[x2][y2].value && !squares[x1][y1].merged && !squares[x2][y2].merged) {
			squares[x2][y2].setValue(squares[x2][y2].value * 2, true);
			squares[x2][y2].merged = true;
			squares[x1][y1].targetX = squares[x2][y2].targetX;
			squares[x1][y1].targetY = squares[x2][y2].targetY;
			toBeRemoved.push(squares[x1][y1]);
			squares[x1][y1] = null;
			gridContainer.removeChild(squares[x2][y2]);
			gridContainer.addChild(squares[x2][y2]);
			return true;
		}
		return false;
	};
	var gridSize = 4;
	var gridContainer = new Container();
	gridContainer.x = 44;
	gridContainer.y = 400;
	self.addChild(gridContainer);
	for (var i = 0; i < gridSize; i++) {
		for (var j = 0; j < gridSize; j++) {
			var gridTile = gridContainer.createAsset('gridTile', 'Grid Tile', .5, .5);
			gridTile.x = i * 500 + 250;
			gridTile.y = j * 500 + 250;
			gridTile.alpha = .1;
		}
	}
	var squares = new Array(gridSize).fill().map(() => new Array(gridSize).fill(null));
	for (var i = 0; i < gridSize; i++) {
		for (var j = 0; j < gridSize; j++) {
			squares[i][j] = null;
		}
	}
	var initialTiles = 2;
	for (var i = 0; i < initialTiles; i++) {
		var x, y;
		do {
			x = Math.floor(Math.random() * gridSize);
			y = Math.floor(Math.random() * gridSize);
		} while (squares[x][y] !== null);
		var square = new Tile();
		square.setPosition(x, y, true);
		squares[x][y] = square;
		gridContainer.addChild(square);
	}
	var swipeStart = {
		x: 0,
		y: 0
	};
	var swipeEnd = {
		x: 0,
		y: 0
	};
	var boardChanged = false;
	var winMessageShown = false;
	self.moveTiles = function (direction) {
		var dx = (direction === 'right') - (direction === 'left');
		var dy = (direction === 'down') - (direction === 'up');
		var moved = false;
		do {
			moved = false;
			var iStart = dx > 0 ? gridSize - 1 : 0;
			var iEnd = dx > 0 ? -1 : gridSize;
			var iStep = dx > 0 ? -1 : 1;
			var jStart = dy > 0 ? gridSize - 1 : 0;
			var jEnd = dy > 0 ? -1 : gridSize;
			var jStep = dy > 0 ? -1 : 1;
			for (var i = iStart; i !== iEnd; i += iStep) {
				for (var j = jStart; j !== jEnd; j += jStep) {
					var x = i + dx;
					var y = j + dy;
					if (x >= 0 && x < gridSize && y >= 0 && y < gridSize && squares[i][j] !== null) {
						if (squares[x][y] === null) {
							squares[x][y] = squares[i][j];
							squares[i][j] = null;
							squares[x][y].setPosition(x, y);
							moved = true;
							boardChanged = true;
						} else if (squares[x][y] !== null && squares[i][j] !== null && squares[x][y].value === squares[i][j].value) {
							moved = self.mergeTiles(i, j, x, y);
							if (moved) boardChanged = true;
						}
					}
				}
			}
		} while (moved);
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] !== null) {
					squares[i][j].merged = false;
				}
			}
		}
	};
	stage.on('down', function (obj) {
		var anyTileMoving = squares.some(row => row.some(tile => tile && tile.isMoving));
		if (!anyTileMoving) {
			var pos = obj.event.getLocalPosition(self);
			swipeStart = {
				x: pos.x,
				y: pos.y
			};
		}
	});
	stage.on('up', function (obj) {
		var pos = obj.event.getLocalPosition(self);
		swipeEnd = {
			x: pos.x,
			y: pos.y
		};
		var dx = swipeEnd.x - swipeStart.x;
		var dy = swipeEnd.y - swipeStart.y;
		var threshold = 50;
		if (Math.abs(dx) > threshold || Math.abs(dy) > threshold) {
			if (Math.abs(dx) > Math.abs(dy)) {
				if (dx > 0) {
					self.moveTiles('right');
				} else {
					self.moveTiles('left');
				}
			} else {
				if (dy > 0) {
					self.moveTiles('down');
				} else {
					self.moveTiles('up');
				}
			}
			if (tutorialTxt) {
				LK.gui.bottom.removeChild(tutorialTxt);
				tutorialTxt = null;
			}
		}
	});
	LK.on('tick', function () {
		var allTilesStopped = true;
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] !== null) {
					if (!squares[i][j].move()) {
						allTilesStopped = false;
					}
				}
			}
		}
		for (var i = 0; i < toBeRemoved.length; i++) {
			if (!toBeRemoved[i].move()) {
				allTilesStopped = false;
			} else {
				toBeRemoved[i].destroy();
				toBeRemoved.splice(i, 1);
				i--;
			}
		}
		if (allTilesStopped) {
			for (var i = 0; i < gridSize; i++) {
				for (var j = 0; j < gridSize; j++) {
					if (squares[i][j] !== null && squares[i][j].newValue) {
						squares[i][j].updateToRealValue();
						if (squares[i][j].value === 2048 && !winMessageShown) {
							var winMessage = new Text2('Congratulations\n you win!', {
								size: 100,
								fill: '#ffffff',
								font: 'bold',
								dropShadow: true,
								dropShadowColor: '#000000',
								dropShadowBlur: 4,
								dropShadowAngle: Math.PI / 6,
								dropShadowDistance: 6,
								align: 'center'
							});
							winMessage.anchor.set(0.5, 0.5);
							LK.gui.center.addChild(winMessage);
							winMessageShown = true;
							LK.setTimeout(function () {
								LK.gui.center.removeChild(winMessage);
							}, 5000);
						}
					}
				}
			}
			if (boardChanged) {
				self.addRandomTile();
				boardChanged = false;
				var maxTileValue = Math.max.apply(Math, squares.map(function (row) {
					return Math.max.apply(Math, row.map(function (tile) {
						return tile ? tile.value : 0;
					}));
				}));
				scoreTxt.setText(maxTileValue.toString());
			}
			if (!self.canMove()) {
				LK.showGameOver();
			}
		}
	});
});
 var Tile = Container.expand(function (value) {
	var self = Container.call(this);
	self.alpha = 0;
	var fadeIn = function () {
		if (self.alpha < 1) {
			self.alpha = Math.min(self.alpha + 0.24, 1);
			self.scale.set(self.alpha);
			LK.setTimeout(fadeIn, 16.67);
		} else {
			self.alpha = 1;
			self.scale.set(1);
		}
	};
	fadeIn();
	self.value = value || 2;
	var tileGraphics = self.createAsset('tile', 'Tile Graphics', .5, .5);
	var valueToColor = function (value) {
		var colors = [0xFFFFFF, 0xFFE5E5, 0xFFCCCC, 0xFFB2B2, 0xFF9999, 0xFF7F7F, 0xFF6666, 0xFF4C4C, 0xFF3232, 0xFF1919, 0xFF0000];
		var index = Math.min(Math.log2(value) - 1, colors.length - 1);
		return colors[index];
	};
	var tileLabel = new Text2(self.value.toString(), {
		size: 300,
		fill: '#332d28',
		font: 'bold'
	});
	tileLabel.anchor.set(0.5, 0.5);
	tileLabel.x = 0;
	tileLabel.y = -40;
	self.addChild(tileLabel);
	self.setValue = function (value) {
		self.newValue = value;
		tileGraphics.tint = valueToColor(value);
	};
	self.updateToRealValue = function () {
		if (self.newValue) {
			self.value = self.newValue;
			tileLabel.setText(self.value.toString());
			var textScale = Math.min(1, 350 / (tileLabel.width / tileLabel.scale.x));
			tileLabel.scale.set(textScale);
			self.newValue = null;
			self.scale.set(1.2);
			var scaleDown = function () {
				if (self.scale.x > 1) {
					self.scale.x -= 0.02;
					self.scale.y -= 0.02;
					LK.setTimeout(scaleDown, 16.67);
				} else {
					self.scale.set(1);
				}
			};
			scaleDown();
		}
	};
	self.targetX = 0;
	self.targetY = 0;
	self.isMoving = false;
	self.merged = false;
	self.setPosition = function (x, y, instant) {
		self.targetX = x * 500 + 250;
		self.targetY = y * 500 + 250;
		if (instant) {
			self.x = self.targetX;
			self.y = self.targetY;
		}
	};
	self.move = function () {
		var dx = self.targetX - self.x;
		var dy = self.targetY - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 150) {
			self.isMoving = true;
			self.x += dx / distance * 150;
			self.y += dy / distance * 150;
			return false;
		}
		self.isMoving = false;
		self.x = self.targetX;
		self.y = self.targetY;
		return true;
	};
});
var Game = Container.expand(function () {
	var self = Container.call(this);
	self.canMove = function () {
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] === null) {
					return true;
				}
				if (j !== 0 && squares[i][j].value === squares[i][j - 1].value) {
					return true;
				}
				if (i !== 0 && squares[i][j].value === squares[i - 1][j].value) {
					return true;
				}
			}
		}
		return false;
	};
	var scoreTxt = new Text2('2', {
		size: 220,
		fill: '#000000',
		font: 'Impact'
	});
	scoreTxt.anchor.set(.5, 0);
	LK.gui.topCenter.addChild(scoreTxt);
	var tutorialTxt = new Text2('Swipe to merge & merge', {
		size: 120,
		fill: '#000000',
		font: 'Impact'
	});
	tutorialTxt.anchor.set(.5, 1);
	LK.gui.bottom.addChild(tutorialTxt);
	tutorialTxt.y -= 30;
	var toBeRemoved = [];
	self.addRandomTile = function () {
		var emptyPositions = [];
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] === null) {
					emptyPositions.push({
						x: i,
						y: j
					});
				}
			}
		}
		if (emptyPositions.length > 0) {
			var randomPosition = emptyPositions[Math.floor(Math.random() * emptyPositions.length)];
			var square = new Tile(Math.random() < 0.5 ? 2 : 4);
			square.setPosition(randomPosition.x, randomPosition.y, true);
			squares[randomPosition.x][randomPosition.y] = square;
			gridContainer.addChild(square);
		}
	};
	self.mergeTiles = function (x1, y1, x2, y2) {
		if (squares[x1][y1] !== null && squares[x2][y2] !== null && squares[x1][y1].value === squares[x2][y2].value && !squares[x1][y1].merged && !squares[x2][y2].merged) {
			squares[x2][y2].setValue(squares[x2][y2].value * 2, true);
			squares[x2][y2].merged = true;
			squares[x1][y1].targetX = squares[x2][y2].targetX;
			squares[x1][y1].targetY = squares[x2][y2].targetY;
			toBeRemoved.push(squares[x1][y1]);
			squares[x1][y1] = null;
			gridContainer.removeChild(squares[x2][y2]);
			gridContainer.addChild(squares[x2][y2]);
			return true;
		}
		return false;
	};
	var gridSize = 4;
	var gridContainer = new Container();
	gridContainer.x = 44;
	gridContainer.y = 400;
	self.addChild(gridContainer);
	for (var i = 0; i < gridSize; i++) {
		for (var j = 0; j < gridSize; j++) {
			var gridTile = gridContainer.createAsset('gridTile', 'Grid Tile', .5, .5);
			gridTile.x = i * 500 + 250;
			gridTile.y = j * 500 + 250;
			gridTile.alpha = .1;
		}
	}
	var squares = new Array(gridSize).fill().map(() => new Array(gridSize).fill(null));
	for (var i = 0; i < gridSize; i++) {
		for (var j = 0; j < gridSize; j++) {
			squares[i][j] = null;
		}
	}
	var initialTiles = 2;
	for (var i = 0; i < initialTiles; i++) {
		var x, y;
		do {
			x = Math.floor(Math.random() * gridSize);
			y = Math.floor(Math.random() * gridSize);
		} while (squares[x][y] !== null);
		var square = new Tile();
		square.setPosition(x, y, true);
		squares[x][y] = square;
		gridContainer.addChild(square);
	}
	var swipeStart = {
		x: 0,
		y: 0
	};
	var swipeEnd = {
		x: 0,
		y: 0
	};
	var boardChanged = false;
	var winMessageShown = false;
	self.moveTiles = function (direction) {
		var dx = (direction === 'right') - (direction === 'left');
		var dy = (direction === 'down') - (direction === 'up');
		var moved = false;
		do {
			moved = false;
			var iStart = dx > 0 ? gridSize - 1 : 0;
			var iEnd = dx > 0 ? -1 : gridSize;
			var iStep = dx > 0 ? -1 : 1;
			var jStart = dy > 0 ? gridSize - 1 : 0;
			var jEnd = dy > 0 ? -1 : gridSize;
			var jStep = dy > 0 ? -1 : 1;
			for (var i = iStart; i !== iEnd; i += iStep) {
				for (var j = jStart; j !== jEnd; j += jStep) {
					var x = i + dx;
					var y = j + dy;
					if (x >= 0 && x < gridSize && y >= 0 && y < gridSize && squares[i][j] !== null) {
						if (squares[x][y] === null) {
							squares[x][y] = squares[i][j];
							squares[i][j] = null;
							squares[x][y].setPosition(x, y);
							moved = true;
							boardChanged = true;
						} else if (squares[x][y] !== null && squares[i][j] !== null && squares[x][y].value === squares[i][j].value) {
							moved = self.mergeTiles(i, j, x, y);
							if (moved) boardChanged = true;
						}
					}
				}
			}
		} while (moved);
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] !== null) {
					squares[i][j].merged = false;
				}
			}
		}
	};
	stage.on('down', function (obj) {
		var anyTileMoving = squares.some(row => row.some(tile => tile && tile.isMoving));
		if (!anyTileMoving) {
			var pos = obj.event.getLocalPosition(self);
			swipeStart = {
				x: pos.x,
				y: pos.y
			};
		}
	});
	stage.on('up', function (obj) {
		var pos = obj.event.getLocalPosition(self);
		swipeEnd = {
			x: pos.x,
			y: pos.y
		};
		var dx = swipeEnd.x - swipeStart.x;
		var dy = swipeEnd.y - swipeStart.y;
		var threshold = 50;
		if (Math.abs(dx) > threshold || Math.abs(dy) > threshold) {
			if (Math.abs(dx) > Math.abs(dy)) {
				if (dx > 0) {
					self.moveTiles('right');
				} else {
					self.moveTiles('left');
				}
			} else {
				if (dy > 0) {
					self.moveTiles('down');
				} else {
					self.moveTiles('up');
				}
			}
			if (tutorialTxt) {
				LK.gui.bottom.removeChild(tutorialTxt);
				tutorialTxt = null;
			}
		}
	});
	LK.on('tick', function () {
		var allTilesStopped = true;
		for (var i = 0; i < gridSize; i++) {
			for (var j = 0; j < gridSize; j++) {
				if (squares[i][j] !== null) {
					if (!squares[i][j].move()) {
						allTilesStopped = false;
					}
				}
			}
		}
		for (var i = 0; i < toBeRemoved.length; i++) {
			if (!toBeRemoved[i].move()) {
				allTilesStopped = false;
			} else {
				toBeRemoved[i].destroy();
				toBeRemoved.splice(i, 1);
				i--;
			}
		}
		if (allTilesStopped) {
			for (var i = 0; i < gridSize; i++) {
				for (var j = 0; j < gridSize; j++) {
					if (squares[i][j] !== null && squares[i][j].newValue) {
						squares[i][j].updateToRealValue();
						if (squares[i][j].value === 2048 && !winMessageShown) {
							var winMessage = new Text2('Congratulations\n you win!', {
								size: 100,
								fill: '#ffffff',
								font: 'bold',
								dropShadow: true,
								dropShadowColor: '#000000',
								dropShadowBlur: 4,
								dropShadowAngle: Math.PI / 6,
								dropShadowDistance: 6,
								align: 'center'
							});
							winMessage.anchor.set(0.5, 0.5);
							LK.gui.center.addChild(winMessage);
							winMessageShown = true;
							LK.setTimeout(function () {
								LK.gui.center.removeChild(winMessage);
							}, 5000);
						}
					}
				}
			}
			if (boardChanged) {
				self.addRandomTile();
				boardChanged = false;
				var maxTileValue = Math.max.apply(Math, squares.map(function (row) {
					return Math.max.apply(Math, row.map(function (tile) {
						return tile ? tile.value : 0;
					}));
				}));
				scoreTxt.setText(maxTileValue.toString());
			}
			if (!self.canMove()) {
				LK.showGameOver();
			}
		}
	});
});