/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Food = Container.expand(function (isBonus) {
var self = Container.call(this);
var foodGraphics;
if (isBonus) {
foodGraphics = self.attachAsset('bonusFood', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 5;
self.isBonus = true;
} else {
foodGraphics = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 1;
self.isBonus = false;
}
self.gridX = 0;
self.gridY = 0;
return self;
});
var SnakeSegment = Container.expand(function (isHead) {
var self = Container.call(this);
var segmentGraphics;
if (isHead) {
segmentGraphics = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
segmentGraphics = self.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.gridX = 0;
self.gridY = 0;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
// Game constants
var GRID_SIZE = 40;
var GRID_WIDTH = 48; // 2048 / 40 = 51.2, using 48 for better centering
var GRID_HEIGHT = 60; // 2732 / 40 = 68.3, using 60 for better centering
var MOVE_SPEED = 8; // Ticks between moves
var INITIAL_LENGTH = 3;
// Game variables
var snake = [];
var direction = {
x: 1,
y: 0
}; // Moving right initially
var nextDirection = {
x: 1,
y: 0
};
var food = null;
var bonusFood = null;
var walls = [];
var moveCounter = 0;
var currentSpeed = MOVE_SPEED;
var bonusFoodTimer = 0;
var speedBoostTimer = 0;
// Calculate grid offset for centering
var gridOffsetX = (2048 - GRID_WIDTH * GRID_SIZE) / 2;
var gridOffsetY = (2732 - GRID_HEIGHT * GRID_SIZE) / 2;
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Initialize walls (border)
function createWalls() {
// Top and bottom walls
for (var x = 0; x < GRID_WIDTH; x++) {
// Top wall
var topWall = new Wall();
topWall.gridX = x;
topWall.gridY = 0;
topWall.x = gridOffsetX + x * GRID_SIZE + GRID_SIZE / 2;
topWall.y = gridOffsetY + GRID_SIZE / 2;
walls.push(topWall);
game.addChild(topWall);
// Bottom wall
var bottomWall = new Wall();
bottomWall.gridX = x;
bottomWall.gridY = GRID_HEIGHT - 1;
bottomWall.x = gridOffsetX + x * GRID_SIZE + GRID_SIZE / 2;
bottomWall.y = gridOffsetY + (GRID_HEIGHT - 1) * GRID_SIZE + GRID_SIZE / 2;
walls.push(bottomWall);
game.addChild(bottomWall);
}
// Left and right walls
for (var y = 1; y < GRID_HEIGHT - 1; y++) {
// Left wall
var leftWall = new Wall();
leftWall.gridX = 0;
leftWall.gridY = y;
leftWall.x = gridOffsetX + GRID_SIZE / 2;
leftWall.y = gridOffsetY + y * GRID_SIZE + GRID_SIZE / 2;
walls.push(leftWall);
game.addChild(leftWall);
// Right wall
var rightWall = new Wall();
rightWall.gridX = GRID_WIDTH - 1;
rightWall.gridY = y;
rightWall.x = gridOffsetX + (GRID_WIDTH - 1) * GRID_SIZE + GRID_SIZE / 2;
rightWall.y = gridOffsetY + y * GRID_SIZE + GRID_SIZE / 2;
walls.push(rightWall);
game.addChild(rightWall);
}
}
// Initialize snake
function createSnake() {
var startX = Math.floor(GRID_WIDTH / 2);
var startY = Math.floor(GRID_HEIGHT / 2);
for (var i = 0; i < INITIAL_LENGTH; i++) {
var segment = new SnakeSegment(i === 0);
segment.gridX = startX - i;
segment.gridY = startY;
segment.x = gridOffsetX + segment.gridX * GRID_SIZE + GRID_SIZE / 2;
segment.y = gridOffsetY + segment.gridY * GRID_SIZE + GRID_SIZE / 2;
snake.push(segment);
game.addChild(segment);
}
}
// Spawn regular food
function spawnFood() {
if (food) {
food.destroy();
food = null;
}
var validPositions = [];
for (var x = 1; x < GRID_WIDTH - 1; x++) {
for (var y = 1; y < GRID_HEIGHT - 1; y++) {
var occupied = false;
// Check snake segments
for (var i = 0; i < snake.length; i++) {
if (snake[i].gridX === x && snake[i].gridY === y) {
occupied = true;
break;
}
}
// Check bonus food
if (bonusFood && bonusFood.gridX === x && bonusFood.gridY === y) {
occupied = true;
}
if (!occupied) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var pos = validPositions[Math.floor(Math.random() * validPositions.length)];
food = new Food(false);
food.gridX = pos.x;
food.gridY = pos.y;
food.x = gridOffsetX + pos.x * GRID_SIZE + GRID_SIZE / 2;
food.y = gridOffsetY + pos.y * GRID_SIZE + GRID_SIZE / 2;
game.addChild(food);
}
}
// Spawn bonus food
function spawnBonusFood() {
if (bonusFood) {
bonusFood.destroy();
bonusFood = null;
}
var validPositions = [];
for (var x = 1; x < GRID_WIDTH - 1; x++) {
for (var y = 1; y < GRID_HEIGHT - 1; y++) {
var occupied = false;
// Check snake segments
for (var i = 0; i < snake.length; i++) {
if (snake[i].gridX === x && snake[i].gridY === y) {
occupied = true;
break;
}
}
// Check regular food
if (food && food.gridX === x && food.gridY === y) {
occupied = true;
}
if (!occupied) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var pos = validPositions[Math.floor(Math.random() * validPositions.length)];
bonusFood = new Food(true);
bonusFood.gridX = pos.x;
bonusFood.gridY = pos.y;
bonusFood.x = gridOffsetX + pos.x * GRID_SIZE + GRID_SIZE / 2;
bonusFood.y = gridOffsetY + pos.y * GRID_SIZE + GRID_SIZE / 2;
game.addChild(bonusFood);
// Bonus food flashing effect
tween(bonusFood, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (bonusFood) {
tween(bonusFood, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
}
// Move snake
function moveSnake() {
// Update direction
direction.x = nextDirection.x;
direction.y = nextDirection.y;
// Calculate new head position
var head = snake[0];
var newX = head.gridX + direction.x;
var newY = head.gridY + direction.y;
// Check wall collision
if (newX <= 0 || newX >= GRID_WIDTH - 1 || newY <= 0 || newY >= GRID_HEIGHT - 1) {
LK.showGameOver();
return;
}
// Check self collision
for (var i = 0; i < snake.length; i++) {
if (snake[i].gridX === newX && snake[i].gridY === newY) {
LK.showGameOver();
return;
}
}
// Check food collision
var ateFood = false;
var ateBonus = false;
if (food && food.gridX === newX && food.gridY === newY) {
ateFood = true;
LK.setScore(LK.getScore() + food.points);
LK.getSound('eat').play();
food.destroy();
food = null;
spawnFood();
}
if (bonusFood && bonusFood.gridX === newX && bonusFood.gridY === newY) {
ateBonus = true;
LK.setScore(LK.getScore() + bonusFood.points);
LK.getSound('bonus').play();
bonusFood.destroy();
bonusFood = null;
bonusFoodTimer = 0;
// Speed boost
speedBoostTimer = 300; // 5 seconds at 60fps
currentSpeed = Math.max(2, MOVE_SPEED - 4);
}
// Create new head
var newHead = new SnakeSegment(true);
newHead.gridX = newX;
newHead.gridY = newY;
newHead.x = gridOffsetX + newX * GRID_SIZE + GRID_SIZE / 2;
newHead.y = gridOffsetY + newY * GRID_SIZE + GRID_SIZE / 2;
// Convert old head to body
var oldHead = snake[0];
oldHead.removeChild(oldHead.children[0]);
var bodyGraphics = oldHead.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
snake.unshift(newHead);
game.addChild(newHead);
// Remove tail if no food eaten
if (!ateFood && !ateBonus) {
var tail = snake.pop();
tail.destroy();
}
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
// Increase speed based on score
var baseSpeed = Math.max(3, MOVE_SPEED - Math.floor(LK.getScore() / 10));
if (speedBoostTimer <= 0) {
currentSpeed = baseSpeed;
}
}
// Touch controls
var touchStartX = 0;
var touchStartY = 0;
var minSwipeDistance = 50;
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
};
game.up = function (x, y, obj) {
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance >= minSwipeDistance) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// Horizontal swipe
if (deltaX > 0 && direction.x !== -1) {
nextDirection = {
x: 1,
y: 0
}; // Right
} else if (deltaX < 0 && direction.x !== 1) {
nextDirection = {
x: -1,
y: 0
}; // Left
}
} else {
// Vertical swipe
if (deltaY > 0 && direction.y !== -1) {
nextDirection = {
x: 0,
y: 1
}; // Down
} else if (deltaY < 0 && direction.y !== 1) {
nextDirection = {
x: 0,
y: -1
}; // Up
}
}
}
};
// Initialize game elements
createWalls();
createSnake();
spawnFood();
// Play background music
LK.playMusic('gameMusic');
// Game update loop
game.update = function () {
moveCounter++;
// Move snake
if (moveCounter >= currentSpeed) {
moveSnake();
moveCounter = 0;
}
// Update speed boost timer
if (speedBoostTimer > 0) {
speedBoostTimer--;
}
// Spawn bonus food occasionally
bonusFoodTimer++;
if (bonusFoodTimer >= 600 && !bonusFood && Math.random() < 0.02) {
// 10 seconds minimum, 2% chance per tick
spawnBonusFood();
bonusFoodTimer = 0;
}
// Remove bonus food after some time
if (bonusFood && bonusFoodTimer >= 900) {
// 15 seconds
bonusFood.destroy();
bonusFood = null;
bonusFoodTimer = 0;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Food = Container.expand(function (isBonus) {
var self = Container.call(this);
var foodGraphics;
if (isBonus) {
foodGraphics = self.attachAsset('bonusFood', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 5;
self.isBonus = true;
} else {
foodGraphics = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 1;
self.isBonus = false;
}
self.gridX = 0;
self.gridY = 0;
return self;
});
var SnakeSegment = Container.expand(function (isHead) {
var self = Container.call(this);
var segmentGraphics;
if (isHead) {
segmentGraphics = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
segmentGraphics = self.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.gridX = 0;
self.gridY = 0;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
// Game constants
var GRID_SIZE = 40;
var GRID_WIDTH = 48; // 2048 / 40 = 51.2, using 48 for better centering
var GRID_HEIGHT = 60; // 2732 / 40 = 68.3, using 60 for better centering
var MOVE_SPEED = 8; // Ticks between moves
var INITIAL_LENGTH = 3;
// Game variables
var snake = [];
var direction = {
x: 1,
y: 0
}; // Moving right initially
var nextDirection = {
x: 1,
y: 0
};
var food = null;
var bonusFood = null;
var walls = [];
var moveCounter = 0;
var currentSpeed = MOVE_SPEED;
var bonusFoodTimer = 0;
var speedBoostTimer = 0;
// Calculate grid offset for centering
var gridOffsetX = (2048 - GRID_WIDTH * GRID_SIZE) / 2;
var gridOffsetY = (2732 - GRID_HEIGHT * GRID_SIZE) / 2;
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Initialize walls (border)
function createWalls() {
// Top and bottom walls
for (var x = 0; x < GRID_WIDTH; x++) {
// Top wall
var topWall = new Wall();
topWall.gridX = x;
topWall.gridY = 0;
topWall.x = gridOffsetX + x * GRID_SIZE + GRID_SIZE / 2;
topWall.y = gridOffsetY + GRID_SIZE / 2;
walls.push(topWall);
game.addChild(topWall);
// Bottom wall
var bottomWall = new Wall();
bottomWall.gridX = x;
bottomWall.gridY = GRID_HEIGHT - 1;
bottomWall.x = gridOffsetX + x * GRID_SIZE + GRID_SIZE / 2;
bottomWall.y = gridOffsetY + (GRID_HEIGHT - 1) * GRID_SIZE + GRID_SIZE / 2;
walls.push(bottomWall);
game.addChild(bottomWall);
}
// Left and right walls
for (var y = 1; y < GRID_HEIGHT - 1; y++) {
// Left wall
var leftWall = new Wall();
leftWall.gridX = 0;
leftWall.gridY = y;
leftWall.x = gridOffsetX + GRID_SIZE / 2;
leftWall.y = gridOffsetY + y * GRID_SIZE + GRID_SIZE / 2;
walls.push(leftWall);
game.addChild(leftWall);
// Right wall
var rightWall = new Wall();
rightWall.gridX = GRID_WIDTH - 1;
rightWall.gridY = y;
rightWall.x = gridOffsetX + (GRID_WIDTH - 1) * GRID_SIZE + GRID_SIZE / 2;
rightWall.y = gridOffsetY + y * GRID_SIZE + GRID_SIZE / 2;
walls.push(rightWall);
game.addChild(rightWall);
}
}
// Initialize snake
function createSnake() {
var startX = Math.floor(GRID_WIDTH / 2);
var startY = Math.floor(GRID_HEIGHT / 2);
for (var i = 0; i < INITIAL_LENGTH; i++) {
var segment = new SnakeSegment(i === 0);
segment.gridX = startX - i;
segment.gridY = startY;
segment.x = gridOffsetX + segment.gridX * GRID_SIZE + GRID_SIZE / 2;
segment.y = gridOffsetY + segment.gridY * GRID_SIZE + GRID_SIZE / 2;
snake.push(segment);
game.addChild(segment);
}
}
// Spawn regular food
function spawnFood() {
if (food) {
food.destroy();
food = null;
}
var validPositions = [];
for (var x = 1; x < GRID_WIDTH - 1; x++) {
for (var y = 1; y < GRID_HEIGHT - 1; y++) {
var occupied = false;
// Check snake segments
for (var i = 0; i < snake.length; i++) {
if (snake[i].gridX === x && snake[i].gridY === y) {
occupied = true;
break;
}
}
// Check bonus food
if (bonusFood && bonusFood.gridX === x && bonusFood.gridY === y) {
occupied = true;
}
if (!occupied) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var pos = validPositions[Math.floor(Math.random() * validPositions.length)];
food = new Food(false);
food.gridX = pos.x;
food.gridY = pos.y;
food.x = gridOffsetX + pos.x * GRID_SIZE + GRID_SIZE / 2;
food.y = gridOffsetY + pos.y * GRID_SIZE + GRID_SIZE / 2;
game.addChild(food);
}
}
// Spawn bonus food
function spawnBonusFood() {
if (bonusFood) {
bonusFood.destroy();
bonusFood = null;
}
var validPositions = [];
for (var x = 1; x < GRID_WIDTH - 1; x++) {
for (var y = 1; y < GRID_HEIGHT - 1; y++) {
var occupied = false;
// Check snake segments
for (var i = 0; i < snake.length; i++) {
if (snake[i].gridX === x && snake[i].gridY === y) {
occupied = true;
break;
}
}
// Check regular food
if (food && food.gridX === x && food.gridY === y) {
occupied = true;
}
if (!occupied) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var pos = validPositions[Math.floor(Math.random() * validPositions.length)];
bonusFood = new Food(true);
bonusFood.gridX = pos.x;
bonusFood.gridY = pos.y;
bonusFood.x = gridOffsetX + pos.x * GRID_SIZE + GRID_SIZE / 2;
bonusFood.y = gridOffsetY + pos.y * GRID_SIZE + GRID_SIZE / 2;
game.addChild(bonusFood);
// Bonus food flashing effect
tween(bonusFood, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (bonusFood) {
tween(bonusFood, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
}
// Move snake
function moveSnake() {
// Update direction
direction.x = nextDirection.x;
direction.y = nextDirection.y;
// Calculate new head position
var head = snake[0];
var newX = head.gridX + direction.x;
var newY = head.gridY + direction.y;
// Check wall collision
if (newX <= 0 || newX >= GRID_WIDTH - 1 || newY <= 0 || newY >= GRID_HEIGHT - 1) {
LK.showGameOver();
return;
}
// Check self collision
for (var i = 0; i < snake.length; i++) {
if (snake[i].gridX === newX && snake[i].gridY === newY) {
LK.showGameOver();
return;
}
}
// Check food collision
var ateFood = false;
var ateBonus = false;
if (food && food.gridX === newX && food.gridY === newY) {
ateFood = true;
LK.setScore(LK.getScore() + food.points);
LK.getSound('eat').play();
food.destroy();
food = null;
spawnFood();
}
if (bonusFood && bonusFood.gridX === newX && bonusFood.gridY === newY) {
ateBonus = true;
LK.setScore(LK.getScore() + bonusFood.points);
LK.getSound('bonus').play();
bonusFood.destroy();
bonusFood = null;
bonusFoodTimer = 0;
// Speed boost
speedBoostTimer = 300; // 5 seconds at 60fps
currentSpeed = Math.max(2, MOVE_SPEED - 4);
}
// Create new head
var newHead = new SnakeSegment(true);
newHead.gridX = newX;
newHead.gridY = newY;
newHead.x = gridOffsetX + newX * GRID_SIZE + GRID_SIZE / 2;
newHead.y = gridOffsetY + newY * GRID_SIZE + GRID_SIZE / 2;
// Convert old head to body
var oldHead = snake[0];
oldHead.removeChild(oldHead.children[0]);
var bodyGraphics = oldHead.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
snake.unshift(newHead);
game.addChild(newHead);
// Remove tail if no food eaten
if (!ateFood && !ateBonus) {
var tail = snake.pop();
tail.destroy();
}
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
// Increase speed based on score
var baseSpeed = Math.max(3, MOVE_SPEED - Math.floor(LK.getScore() / 10));
if (speedBoostTimer <= 0) {
currentSpeed = baseSpeed;
}
}
// Touch controls
var touchStartX = 0;
var touchStartY = 0;
var minSwipeDistance = 50;
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
};
game.up = function (x, y, obj) {
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance >= minSwipeDistance) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// Horizontal swipe
if (deltaX > 0 && direction.x !== -1) {
nextDirection = {
x: 1,
y: 0
}; // Right
} else if (deltaX < 0 && direction.x !== 1) {
nextDirection = {
x: -1,
y: 0
}; // Left
}
} else {
// Vertical swipe
if (deltaY > 0 && direction.y !== -1) {
nextDirection = {
x: 0,
y: 1
}; // Down
} else if (deltaY < 0 && direction.y !== 1) {
nextDirection = {
x: 0,
y: -1
}; // Up
}
}
}
};
// Initialize game elements
createWalls();
createSnake();
spawnFood();
// Play background music
LK.playMusic('gameMusic');
// Game update loop
game.update = function () {
moveCounter++;
// Move snake
if (moveCounter >= currentSpeed) {
moveSnake();
moveCounter = 0;
}
// Update speed boost timer
if (speedBoostTimer > 0) {
speedBoostTimer--;
}
// Spawn bonus food occasionally
bonusFoodTimer++;
if (bonusFoodTimer >= 600 && !bonusFood && Math.random() < 0.02) {
// 10 seconds minimum, 2% chance per tick
spawnBonusFood();
bonusFoodTimer = 0;
}
// Remove bonus food after some time
if (bonusFood && bonusFoodTimer >= 900) {
// 15 seconds
bonusFood.destroy();
bonusFood = null;
bonusFoodTimer = 0;
}
};