/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Food = Container.expand(function () {
var self = Container.call(this);
// Create food graphics
self.graphics = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
// Animation to make food more noticeable
self.update = function () {
self.graphics.rotation += 0.03;
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
// Create portal graphics
self.graphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
// Animation to make portal more noticeable
self.update = function () {
self.graphics.rotation += 0.05;
// Pulsing effect
if (!self.pulseDirection) {
self.pulseDirection = 1;
self.pulseValue = 0;
}
self.pulseValue += 0.02 * self.pulseDirection;
if (self.pulseValue >= 1) {
self.pulseDirection = -1;
} else if (self.pulseValue <= 0) {
self.pulseDirection = 1;
}
// Scale between 0.9 and 1.1
var scale = 0.9 + self.pulseValue * 0.2;
self.graphics.scale.set(scale, scale);
};
return self;
});
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
// Default to body segment
self.isHead = false;
// Create segment graphics
self.graphics = self.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Set as head segment if needed
self.setAsHead = function () {
self.isHead = true;
self.removeChild(self.graphics);
self.graphics = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
};
// Store previous position for smooth movement
self.prevX = 0;
self.prevY = 0;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
// Create wall graphics
self.graphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x202020
});
/****
* Game Code
****/
// Game constants
var GRID_SIZE = 50;
var GRID_WIDTH = Math.floor(2048 / GRID_SIZE);
var GRID_HEIGHT = Math.floor(2732 / GRID_SIZE);
var GAME_SPEED = 200; // ms between moves
var INITIAL_SNAKE_LENGTH = 3;
// Game variables
var snake = [];
var walls = [];
var foods = [];
var portal = null;
var direction = 'right';
var nextDirection = 'right';
var moveTimer = null;
var level = storage.level || 1;
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
var gameBoard = new Container();
var gameRunning = false;
var levelText;
var scoreText;
// Add game board to center the maze
game.addChild(gameBoard);
// Initialize the game UI
function initUI() {
// Level display
levelText = new Text2('Level: ' + level, {
size: 70,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
LK.gui.topRight.addChild(levelText);
// Score display
scoreText = new Text2('Score: ' + LK.getScore(), {
size: 70,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
}
// Reset the game for a new level
function resetGame() {
// Clear existing elements
while (gameBoard.children.length > 0) {
gameBoard.removeChildAt(0);
}
// Reset arrays
snake = [];
walls = [];
foods = [];
portal = null;
// Reset direction
direction = 'right';
nextDirection = 'right';
// Clear timers
if (moveTimer) {
LK.clearInterval(moveTimer);
moveTimer = null;
}
// Generate level
generateLevel(level);
// Start the game loop
moveTimer = LK.setInterval(moveSnake, GAME_SPEED);
gameRunning = true;
// Update UI
levelText.setText('Level: ' + level);
scoreText.setText('Score: ' + LK.getScore());
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// Generate a level with walls, food, and a snake
function generateLevel(levelNum) {
var difficulty = Math.min(10, Math.floor(levelNum / 2) + 1);
// Calculate board size (smaller than full screen)
var boardWidth = Math.min(GRID_WIDTH - 2, 20) * GRID_SIZE;
var boardHeight = Math.min(GRID_HEIGHT - 6, 20) * GRID_SIZE;
// Center the board
gameBoard.x = (2048 - boardWidth) / 2;
gameBoard.y = (2732 - boardHeight) / 2;
// Generate walls around the perimeter
for (var x = 0; x < boardWidth / GRID_SIZE; x++) {
addWall(x * GRID_SIZE, 0);
addWall(x * GRID_SIZE, boardHeight - GRID_SIZE);
}
for (var y = 0; y < boardHeight / GRID_SIZE; y++) {
addWall(0, y * GRID_SIZE);
addWall(boardWidth - GRID_SIZE, y * GRID_SIZE);
}
// Generate internal walls based on difficulty
for (var i = 0; i < difficulty * 5; i++) {
var wx = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 4) + 2) * GRID_SIZE;
var wy = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 4) + 2) * GRID_SIZE;
// Ensure we're not placing walls too close to starting position
if (Math.abs(wx - 4 * GRID_SIZE) > 2 * GRID_SIZE || Math.abs(wy - 4 * GRID_SIZE) > 2 * GRID_SIZE) {
addWall(wx, wy);
}
}
// Create snake at starting position
createSnake(3 * GRID_SIZE, 4 * GRID_SIZE, INITIAL_SNAKE_LENGTH);
// Add food
for (var f = 0; f < difficulty + 2; f++) {
addRandomFood(boardWidth, boardHeight);
}
// Add exit portal
var px = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 4) + 2) * GRID_SIZE;
var py = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 4) + 2) * GRID_SIZE;
// Make sure portal isn't too close to snake start
while (Math.abs(px - 4 * GRID_SIZE) < 5 * GRID_SIZE && Math.abs(py - 4 * GRID_SIZE) < 5 * GRID_SIZE) {
px = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 4) + 2) * GRID_SIZE;
py = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 4) + 2) * GRID_SIZE;
}
addPortal(px, py);
}
// Create the initial snake
function createSnake(x, y, length) {
for (var i = 0; i < length; i++) {
var segment = new SnakeSegment();
// Place segment with head at (x,y) and body extending to the left
segment.x = x - i * GRID_SIZE;
segment.y = y;
segment.prevX = segment.x;
segment.prevY = segment.y;
// Make the first segment the head
if (i === 0) {
segment.setAsHead();
}
snake.push(segment);
gameBoard.addChild(segment);
}
}
// Add a wall at the specified position
function addWall(x, y) {
var wall = new Wall();
wall.x = x;
wall.y = y;
walls.push(wall);
gameBoard.addChild(wall);
}
// Add food at a random position
function addRandomFood(boardWidth, boardHeight) {
var validPosition = false;
var fx, fy;
while (!validPosition) {
fx = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 2) + 1) * GRID_SIZE;
fy = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 2) + 1) * GRID_SIZE;
validPosition = true;
// Check if position overlaps with walls
for (var i = 0; i < walls.length; i++) {
if (walls[i].x === fx && walls[i].y === fy) {
validPosition = false;
break;
}
}
// Check if position overlaps with snake
if (validPosition) {
for (var j = 0; j < snake.length; j++) {
if (Math.abs(snake[j].x - fx) < GRID_SIZE && Math.abs(snake[j].y - fy) < GRID_SIZE) {
validPosition = false;
break;
}
}
}
// Check if position overlaps with portal
if (validPosition && portal && Math.abs(portal.x - fx) < GRID_SIZE && Math.abs(portal.y - fy) < GRID_SIZE) {
validPosition = false;
}
}
var food = new Food();
food.x = fx;
food.y = fy;
foods.push(food);
gameBoard.addChild(food);
}
// Add exit portal
function addPortal(x, y) {
portal = new Portal();
portal.x = x;
portal.y = y;
gameBoard.addChild(portal);
}
// Move the snake based on current direction
function moveSnake() {
if (!gameRunning) return;
// Update direction
direction = nextDirection;
// Store previous positions for smooth movement
for (var i = 0; i < snake.length; i++) {
snake[i].prevX = snake[i].x;
snake[i].prevY = snake[i].y;
}
// Calculate new head position
var head = snake[0];
var newHeadX = head.x;
var newHeadY = head.y;
switch (direction) {
case 'up':
newHeadY -= GRID_SIZE;
break;
case 'down':
newHeadY += GRID_SIZE;
break;
case 'left':
newHeadX -= GRID_SIZE;
break;
case 'right':
newHeadX += GRID_SIZE;
break;
}
// Check for collisions
if (checkCollision(newHeadX, newHeadY)) {
// Game over
LK.getSound('hit').play();
gameRunning = false;
// Flash red and show game over
LK.effects.flashScreen(0xFF0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
}, 600);
return;
}
// Move body segments
for (var i = snake.length - 1; i > 0; i--) {
snake[i].x = snake[i - 1].x;
snake[i].y = snake[i - 1].y;
}
// Move head to new position
head.x = newHeadX;
head.y = newHeadY;
// Check for food collision
for (var i = 0; i < foods.length; i++) {
if (Math.abs(head.x - foods[i].x) < GRID_SIZE / 2 && Math.abs(head.y - foods[i].y) < GRID_SIZE / 2) {
// Remove the food
gameBoard.removeChild(foods[i]);
foods.splice(i, 1);
// Play eat sound
LK.getSound('eat').play();
// Add score
LK.setScore(LK.getScore() + 10);
scoreText.setText('Score: ' + LK.getScore());
// Add a new segment
var tail = snake[snake.length - 1];
var newSegment = new SnakeSegment();
newSegment.x = tail.x;
newSegment.y = tail.y;
newSegment.prevX = newSegment.x;
newSegment.prevY = newSegment.y;
snake.push(newSegment);
gameBoard.addChild(newSegment);
// Add a new food
var boardWidth = Math.min(GRID_WIDTH - 2, 20) * GRID_SIZE;
var boardHeight = Math.min(GRID_HEIGHT - 6, 20) * GRID_SIZE;
addRandomFood(boardWidth, boardHeight);
break;
}
}
// Check for portal collision
if (portal && Math.abs(head.x - portal.x) < GRID_SIZE / 2 && Math.abs(head.y - portal.y) < GRID_SIZE / 2) {
// Play portal sound
LK.getSound('portal').play();
// Level completed
gameRunning = false;
// Flash screen and advance to next level
LK.effects.flashScreen(0x9966FF, 500);
// Store level progress
level++;
storage.level = level;
LK.setTimeout(function () {
resetGame();
}, 1000);
}
}
// Check if the given position collides with walls or snake body
function checkCollision(x, y) {
// Check wall collision
for (var i = 0; i < walls.length; i++) {
if (Math.abs(x - walls[i].x) < GRID_SIZE / 2 && Math.abs(y - walls[i].y) < GRID_SIZE / 2) {
return true;
}
}
// Check self collision (skip head)
for (var i = 1; i < snake.length; i++) {
if (Math.abs(x - snake[i].x) < GRID_SIZE / 2 && Math.abs(y - snake[i].y) < GRID_SIZE / 2) {
return true;
}
}
return false;
}
// Handle touch/mouse down on game
game.down = function (x, y, obj) {
if (!gameRunning) return;
isDragging = true;
dragStartX = x;
dragStartY = y;
};
// Handle touch/mouse move
game.move = function (x, y, obj) {
if (!isDragging || !gameRunning) return;
// Calculate the difference from start position
var dx = x - dragStartX;
var dy = y - dragStartY;
// Only change direction if the drag is significant
if (Math.abs(dx) > 20 || Math.abs(dy) > 20) {
// Determine direction based on which axis has the larger change
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal movement
if (dx > 0 && direction !== 'left') {
nextDirection = 'right';
} else if (dx < 0 && direction !== 'right') {
nextDirection = 'left';
}
} else {
// Vertical movement
if (dy > 0 && direction !== 'up') {
nextDirection = 'down';
} else if (dy < 0 && direction !== 'down') {
nextDirection = 'up';
}
}
// Reset drag start
dragStartX = x;
dragStartY = y;
}
};
// Handle touch/mouse up
game.up = function (x, y, obj) {
isDragging = false;
};
// Update game state each tick
game.update = function () {
// Update all elements with update methods
for (var i = 0; i < foods.length; i++) {
foods[i].update();
}
if (portal) {
portal.update();
}
};
// Initialize UI and start the game
initUI();
resetGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Food = Container.expand(function () {
var self = Container.call(this);
// Create food graphics
self.graphics = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
// Animation to make food more noticeable
self.update = function () {
self.graphics.rotation += 0.03;
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
// Create portal graphics
self.graphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
// Animation to make portal more noticeable
self.update = function () {
self.graphics.rotation += 0.05;
// Pulsing effect
if (!self.pulseDirection) {
self.pulseDirection = 1;
self.pulseValue = 0;
}
self.pulseValue += 0.02 * self.pulseDirection;
if (self.pulseValue >= 1) {
self.pulseDirection = -1;
} else if (self.pulseValue <= 0) {
self.pulseDirection = 1;
}
// Scale between 0.9 and 1.1
var scale = 0.9 + self.pulseValue * 0.2;
self.graphics.scale.set(scale, scale);
};
return self;
});
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
// Default to body segment
self.isHead = false;
// Create segment graphics
self.graphics = self.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Set as head segment if needed
self.setAsHead = function () {
self.isHead = true;
self.removeChild(self.graphics);
self.graphics = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
};
// Store previous position for smooth movement
self.prevX = 0;
self.prevY = 0;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
// Create wall graphics
self.graphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x202020
});
/****
* Game Code
****/
// Game constants
var GRID_SIZE = 50;
var GRID_WIDTH = Math.floor(2048 / GRID_SIZE);
var GRID_HEIGHT = Math.floor(2732 / GRID_SIZE);
var GAME_SPEED = 200; // ms between moves
var INITIAL_SNAKE_LENGTH = 3;
// Game variables
var snake = [];
var walls = [];
var foods = [];
var portal = null;
var direction = 'right';
var nextDirection = 'right';
var moveTimer = null;
var level = storage.level || 1;
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
var gameBoard = new Container();
var gameRunning = false;
var levelText;
var scoreText;
// Add game board to center the maze
game.addChild(gameBoard);
// Initialize the game UI
function initUI() {
// Level display
levelText = new Text2('Level: ' + level, {
size: 70,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
LK.gui.topRight.addChild(levelText);
// Score display
scoreText = new Text2('Score: ' + LK.getScore(), {
size: 70,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
}
// Reset the game for a new level
function resetGame() {
// Clear existing elements
while (gameBoard.children.length > 0) {
gameBoard.removeChildAt(0);
}
// Reset arrays
snake = [];
walls = [];
foods = [];
portal = null;
// Reset direction
direction = 'right';
nextDirection = 'right';
// Clear timers
if (moveTimer) {
LK.clearInterval(moveTimer);
moveTimer = null;
}
// Generate level
generateLevel(level);
// Start the game loop
moveTimer = LK.setInterval(moveSnake, GAME_SPEED);
gameRunning = true;
// Update UI
levelText.setText('Level: ' + level);
scoreText.setText('Score: ' + LK.getScore());
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// Generate a level with walls, food, and a snake
function generateLevel(levelNum) {
var difficulty = Math.min(10, Math.floor(levelNum / 2) + 1);
// Calculate board size (smaller than full screen)
var boardWidth = Math.min(GRID_WIDTH - 2, 20) * GRID_SIZE;
var boardHeight = Math.min(GRID_HEIGHT - 6, 20) * GRID_SIZE;
// Center the board
gameBoard.x = (2048 - boardWidth) / 2;
gameBoard.y = (2732 - boardHeight) / 2;
// Generate walls around the perimeter
for (var x = 0; x < boardWidth / GRID_SIZE; x++) {
addWall(x * GRID_SIZE, 0);
addWall(x * GRID_SIZE, boardHeight - GRID_SIZE);
}
for (var y = 0; y < boardHeight / GRID_SIZE; y++) {
addWall(0, y * GRID_SIZE);
addWall(boardWidth - GRID_SIZE, y * GRID_SIZE);
}
// Generate internal walls based on difficulty
for (var i = 0; i < difficulty * 5; i++) {
var wx = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 4) + 2) * GRID_SIZE;
var wy = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 4) + 2) * GRID_SIZE;
// Ensure we're not placing walls too close to starting position
if (Math.abs(wx - 4 * GRID_SIZE) > 2 * GRID_SIZE || Math.abs(wy - 4 * GRID_SIZE) > 2 * GRID_SIZE) {
addWall(wx, wy);
}
}
// Create snake at starting position
createSnake(3 * GRID_SIZE, 4 * GRID_SIZE, INITIAL_SNAKE_LENGTH);
// Add food
for (var f = 0; f < difficulty + 2; f++) {
addRandomFood(boardWidth, boardHeight);
}
// Add exit portal
var px = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 4) + 2) * GRID_SIZE;
var py = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 4) + 2) * GRID_SIZE;
// Make sure portal isn't too close to snake start
while (Math.abs(px - 4 * GRID_SIZE) < 5 * GRID_SIZE && Math.abs(py - 4 * GRID_SIZE) < 5 * GRID_SIZE) {
px = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 4) + 2) * GRID_SIZE;
py = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 4) + 2) * GRID_SIZE;
}
addPortal(px, py);
}
// Create the initial snake
function createSnake(x, y, length) {
for (var i = 0; i < length; i++) {
var segment = new SnakeSegment();
// Place segment with head at (x,y) and body extending to the left
segment.x = x - i * GRID_SIZE;
segment.y = y;
segment.prevX = segment.x;
segment.prevY = segment.y;
// Make the first segment the head
if (i === 0) {
segment.setAsHead();
}
snake.push(segment);
gameBoard.addChild(segment);
}
}
// Add a wall at the specified position
function addWall(x, y) {
var wall = new Wall();
wall.x = x;
wall.y = y;
walls.push(wall);
gameBoard.addChild(wall);
}
// Add food at a random position
function addRandomFood(boardWidth, boardHeight) {
var validPosition = false;
var fx, fy;
while (!validPosition) {
fx = Math.floor(Math.random() * (boardWidth / GRID_SIZE - 2) + 1) * GRID_SIZE;
fy = Math.floor(Math.random() * (boardHeight / GRID_SIZE - 2) + 1) * GRID_SIZE;
validPosition = true;
// Check if position overlaps with walls
for (var i = 0; i < walls.length; i++) {
if (walls[i].x === fx && walls[i].y === fy) {
validPosition = false;
break;
}
}
// Check if position overlaps with snake
if (validPosition) {
for (var j = 0; j < snake.length; j++) {
if (Math.abs(snake[j].x - fx) < GRID_SIZE && Math.abs(snake[j].y - fy) < GRID_SIZE) {
validPosition = false;
break;
}
}
}
// Check if position overlaps with portal
if (validPosition && portal && Math.abs(portal.x - fx) < GRID_SIZE && Math.abs(portal.y - fy) < GRID_SIZE) {
validPosition = false;
}
}
var food = new Food();
food.x = fx;
food.y = fy;
foods.push(food);
gameBoard.addChild(food);
}
// Add exit portal
function addPortal(x, y) {
portal = new Portal();
portal.x = x;
portal.y = y;
gameBoard.addChild(portal);
}
// Move the snake based on current direction
function moveSnake() {
if (!gameRunning) return;
// Update direction
direction = nextDirection;
// Store previous positions for smooth movement
for (var i = 0; i < snake.length; i++) {
snake[i].prevX = snake[i].x;
snake[i].prevY = snake[i].y;
}
// Calculate new head position
var head = snake[0];
var newHeadX = head.x;
var newHeadY = head.y;
switch (direction) {
case 'up':
newHeadY -= GRID_SIZE;
break;
case 'down':
newHeadY += GRID_SIZE;
break;
case 'left':
newHeadX -= GRID_SIZE;
break;
case 'right':
newHeadX += GRID_SIZE;
break;
}
// Check for collisions
if (checkCollision(newHeadX, newHeadY)) {
// Game over
LK.getSound('hit').play();
gameRunning = false;
// Flash red and show game over
LK.effects.flashScreen(0xFF0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
}, 600);
return;
}
// Move body segments
for (var i = snake.length - 1; i > 0; i--) {
snake[i].x = snake[i - 1].x;
snake[i].y = snake[i - 1].y;
}
// Move head to new position
head.x = newHeadX;
head.y = newHeadY;
// Check for food collision
for (var i = 0; i < foods.length; i++) {
if (Math.abs(head.x - foods[i].x) < GRID_SIZE / 2 && Math.abs(head.y - foods[i].y) < GRID_SIZE / 2) {
// Remove the food
gameBoard.removeChild(foods[i]);
foods.splice(i, 1);
// Play eat sound
LK.getSound('eat').play();
// Add score
LK.setScore(LK.getScore() + 10);
scoreText.setText('Score: ' + LK.getScore());
// Add a new segment
var tail = snake[snake.length - 1];
var newSegment = new SnakeSegment();
newSegment.x = tail.x;
newSegment.y = tail.y;
newSegment.prevX = newSegment.x;
newSegment.prevY = newSegment.y;
snake.push(newSegment);
gameBoard.addChild(newSegment);
// Add a new food
var boardWidth = Math.min(GRID_WIDTH - 2, 20) * GRID_SIZE;
var boardHeight = Math.min(GRID_HEIGHT - 6, 20) * GRID_SIZE;
addRandomFood(boardWidth, boardHeight);
break;
}
}
// Check for portal collision
if (portal && Math.abs(head.x - portal.x) < GRID_SIZE / 2 && Math.abs(head.y - portal.y) < GRID_SIZE / 2) {
// Play portal sound
LK.getSound('portal').play();
// Level completed
gameRunning = false;
// Flash screen and advance to next level
LK.effects.flashScreen(0x9966FF, 500);
// Store level progress
level++;
storage.level = level;
LK.setTimeout(function () {
resetGame();
}, 1000);
}
}
// Check if the given position collides with walls or snake body
function checkCollision(x, y) {
// Check wall collision
for (var i = 0; i < walls.length; i++) {
if (Math.abs(x - walls[i].x) < GRID_SIZE / 2 && Math.abs(y - walls[i].y) < GRID_SIZE / 2) {
return true;
}
}
// Check self collision (skip head)
for (var i = 1; i < snake.length; i++) {
if (Math.abs(x - snake[i].x) < GRID_SIZE / 2 && Math.abs(y - snake[i].y) < GRID_SIZE / 2) {
return true;
}
}
return false;
}
// Handle touch/mouse down on game
game.down = function (x, y, obj) {
if (!gameRunning) return;
isDragging = true;
dragStartX = x;
dragStartY = y;
};
// Handle touch/mouse move
game.move = function (x, y, obj) {
if (!isDragging || !gameRunning) return;
// Calculate the difference from start position
var dx = x - dragStartX;
var dy = y - dragStartY;
// Only change direction if the drag is significant
if (Math.abs(dx) > 20 || Math.abs(dy) > 20) {
// Determine direction based on which axis has the larger change
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal movement
if (dx > 0 && direction !== 'left') {
nextDirection = 'right';
} else if (dx < 0 && direction !== 'right') {
nextDirection = 'left';
}
} else {
// Vertical movement
if (dy > 0 && direction !== 'up') {
nextDirection = 'down';
} else if (dy < 0 && direction !== 'down') {
nextDirection = 'up';
}
}
// Reset drag start
dragStartX = x;
dragStartY = y;
}
};
// Handle touch/mouse up
game.up = function (x, y, obj) {
isDragging = false;
};
// Update game state each tick
game.update = function () {
// Update all elements with update methods
for (var i = 0; i < foods.length; i++) {
foods[i].update();
}
if (portal) {
portal.update();
}
};
// Initialize UI and start the game
initUI();
resetGame();