User prompt
The two fruits are spawning in the same place.
User prompt
If we click behind while the snake is looking forward, it will go back.
User prompt
Let it go where I stepped, but don't go diagonally.
User prompt
make the game's map grid-like
User prompt
Make a separate accessory for the snake's head and prepare 5 different foods. Each one should give a different score and display the score they gave on the screen.
Code edit (1 edits merged)
Please save this source code
User prompt
Snake Classic
Initial prompt
make a snake game
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Food Class
var Food = Container.expand(function () {
var self = Container.call(this);
// Food is a red ellipse
var foodAsset = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Snake Segment Class
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
// Each segment is a green box
var segmentAsset = self.attachAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// --- Game Constants ---
// --- Asset Initialization ---
var GRID_SIZE = 80; // Each cell is 80x80 px
var GRID_WIDTH = Math.floor(2048 / GRID_SIZE);
var GRID_HEIGHT = Math.floor(2732 / GRID_SIZE);
var MOVE_INTERVAL = 120; // ms between snake moves
// --- Game State ---
var snake = []; // Array of SnakeSegment
var snakeDir = {
x: 1,
y: 0
}; // Initial direction: right
var nextDir = {
x: 1,
y: 0
}; // Next direction to turn to
var food = null;
var foodPos = {
x: 0,
y: 0
};
var moveTimer = null;
var isAlive = true;
var pendingGrowth = 0;
// --- Score UI ---
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Helper Functions ---
function gridToPos(gx, gy) {
// Center the grid in the game area
var offsetX = Math.floor((2048 - GRID_WIDTH * GRID_SIZE) / 2);
var offsetY = Math.floor((2732 - GRID_HEIGHT * GRID_SIZE) / 2);
return {
x: offsetX + gx * GRID_SIZE + GRID_SIZE / 2,
y: offsetY + gy * GRID_SIZE + GRID_SIZE / 2
};
}
function posToGrid(x, y) {
var offsetX = Math.floor((2048 - GRID_WIDTH * GRID_SIZE) / 2);
var offsetY = Math.floor((2732 - GRID_HEIGHT * GRID_SIZE) / 2);
return {
x: Math.floor((x - offsetX) / GRID_SIZE),
y: Math.floor((y - offsetY) / GRID_SIZE)
};
}
function spawnFood() {
// Remove old food if exists
if (food) {
food.destroy();
food = null;
}
// Find empty cell
var emptyCells = [];
for (var gx = 0; gx < GRID_WIDTH; gx++) {
for (var gy = 0; gy < GRID_HEIGHT; gy++) {
var occupied = false;
for (var i = 0; i < snake.length; i++) {
if (snake[i].gx === gx && snake[i].gy === gy) {
occupied = true;
break;
}
}
if (!occupied) {
emptyCells.push({
x: gx,
y: gy
});
}
}
}
if (emptyCells.length === 0) {
// No space left, player wins!
LK.showYouWin();
return;
}
var idx = Math.floor(Math.random() * emptyCells.length);
foodPos = emptyCells[idx];
food = new Food();
var pos = gridToPos(foodPos.x, foodPos.y);
food.x = pos.x;
food.y = pos.y;
food.gx = foodPos.x;
food.gy = foodPos.y;
game.addChild(food);
}
// --- Snake Initialization ---
function resetGame() {
// Remove old snake
for (var i = 0; i < snake.length; i++) {
snake[i].destroy();
}
snake = [];
// Remove food
if (food) {
food.destroy();
food = null;
}
// Reset state
isAlive = true;
score = 0;
scoreTxt.setText(score);
snakeDir = {
x: 1,
y: 0
};
nextDir = {
x: 1,
y: 0
};
pendingGrowth = 2; // Start with 3 segments
// Place snake in center
var startX = Math.floor(GRID_WIDTH / 2);
var startY = Math.floor(GRID_HEIGHT / 2);
for (var i = 0; i < 3; i++) {
var seg = new SnakeSegment();
seg.gx = startX - i;
seg.gy = startY;
var pos = gridToPos(seg.gx, seg.gy);
seg.x = pos.x;
seg.y = pos.y;
game.addChild(seg);
snake.push(seg);
}
spawnFood();
// Start move timer
if (moveTimer) LK.clearInterval(moveTimer);
moveTimer = LK.setInterval(moveSnake, MOVE_INTERVAL);
}
// --- Snake Movement ---
function moveSnake() {
if (!isAlive) return;
// Update direction
if ((nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y // Prevent 180 turns
) && (nextDir.x !== snakeDir.x || nextDir.y !== snakeDir.y)) {
snakeDir.x = nextDir.x;
snakeDir.y = nextDir.y;
}
// Calculate new head position
var head = snake[0];
var newGX = head.gx + snakeDir.x;
var newGY = head.gy + snakeDir.y;
// Check wall collision
if (newGX < 0 || newGX >= GRID_WIDTH || newGY < 0 || newGY >= GRID_HEIGHT) {
gameOver();
return;
}
// Check self collision
for (var i = 0; i < snake.length; i++) {
if (snake[i].gx === newGX && snake[i].gy === newGY) {
gameOver();
return;
}
}
// Move segments
var prevGX = head.gx;
var prevGY = head.gy;
var prevPos = gridToPos(prevGX, prevGY);
// Move head
head.gx = newGX;
head.gy = newGY;
var newPos = gridToPos(newGX, newGY);
tween(head, {
x: newPos.x,
y: newPos.y
}, {
duration: MOVE_INTERVAL - 10,
easing: tween.linear
});
// Move body
for (var i = 1; i < snake.length; i++) {
var seg = snake[i];
var tmpGX = seg.gx;
var tmpGY = seg.gy;
var tmpPos = gridToPos(tmpGX, tmpGY);
seg.gx = prevGX;
seg.gy = prevGY;
tween(seg, {
x: prevPos.x,
y: prevPos.y
}, {
duration: MOVE_INTERVAL - 10,
easing: tween.linear
});
prevGX = tmpGX;
prevGY = tmpGY;
prevPos = tmpPos;
}
// Growth
if (pendingGrowth > 0) {
var tail = snake[snake.length - 1];
var newSeg = new SnakeSegment();
newSeg.gx = prevGX;
newSeg.gy = prevGY;
var tailPos = gridToPos(prevGX, prevGY);
newSeg.x = tailPos.x;
newSeg.y = tailPos.y;
game.addChild(newSeg);
snake.push(newSeg);
pendingGrowth--;
}
// Check food collision
if (head.gx === food.gx && head.gy === food.gy) {
score++;
scoreTxt.setText(score);
pendingGrowth++;
spawnFood();
}
}
// --- Game Over ---
function gameOver() {
isAlive = false;
LK.effects.flashScreen(0xff0000, 800);
if (moveTimer) LK.clearInterval(moveTimer);
LK.setTimeout(function () {
LK.showGameOver();
}, 800);
}
// --- Input Handling ---
// Touch/drag/swipe: change direction
var dragStart = null;
var dragLast = null;
var dragThreshold = 40; // px
function handleDirectionInput(x, y) {
// Convert to grid
var head = snake[0];
var pos = gridToPos(head.gx, head.gy);
var dx = x - pos.x;
var dy = y - pos.y;
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal
if (dx > dragThreshold && snakeDir.x !== -1) {
nextDir = {
x: 1,
y: 0
};
} else if (dx < -dragThreshold && snakeDir.x !== 1) {
nextDir = {
x: -1,
y: 0
};
}
} else {
// Vertical
if (dy > dragThreshold && snakeDir.y !== -1) {
nextDir = {
x: 0,
y: 1
};
} else if (dy < -dragThreshold && snakeDir.y !== 1) {
nextDir = {
x: 0,
y: -1
};
}
}
}
// Touch down: start drag
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y
};
dragLast = {
x: x,
y: y
};
};
// Touch move: detect swipe
game.move = function (x, y, obj) {
if (!dragStart) return;
var dx = x - dragStart.x;
var dy = y - dragStart.y;
if (Math.abs(dx) > dragThreshold || Math.abs(dy) > dragThreshold) {
handleDirectionInput(x, y);
dragStart = {
x: x,
y: y
}; // Reset for next swipe
}
dragLast = {
x: x,
y: y
};
};
// Touch up: tap to turn (if no swipe)
game.up = function (x, y, obj) {
if (!dragStart) return;
var dx = x - dragStart.x;
var dy = y - dragStart.y;
if (Math.abs(dx) < dragThreshold && Math.abs(dy) < dragThreshold) {
// Tap: turn clockwise
if (snakeDir.x === 1 && snakeDir.y === 0) nextDir = {
x: 0,
y: 1
};else if (snakeDir.x === 0 && snakeDir.y === 1) nextDir = {
x: -1,
y: 0
};else if (snakeDir.x === -1 && snakeDir.y === 0) nextDir = {
x: 0,
y: -1
};else if (snakeDir.x === 0 && snakeDir.y === -1) nextDir = {
x: 1,
y: 0
};
}
dragStart = null;
dragLast = null;
};
// --- Game Update (not used for movement, but could be used for future features) ---
game.update = function () {
// No per-frame logic needed for MVP
};
// --- Start Game ---
resetGame(); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,358 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Food Class
+var Food = Container.expand(function () {
+ var self = Container.call(this);
+ // Food is a red ellipse
+ var foodAsset = self.attachAsset('food', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ return self;
+});
+// Snake Segment Class
+var SnakeSegment = Container.expand(function () {
+ var self = Container.call(this);
+ // Each segment is a green box
+ var segmentAsset = self.attachAsset('snakeSegment', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x222222
+});
+
+/****
+* Game Code
+****/
+// --- Game Constants ---
+// --- Asset Initialization ---
+var GRID_SIZE = 80; // Each cell is 80x80 px
+var GRID_WIDTH = Math.floor(2048 / GRID_SIZE);
+var GRID_HEIGHT = Math.floor(2732 / GRID_SIZE);
+var MOVE_INTERVAL = 120; // ms between snake moves
+// --- Game State ---
+var snake = []; // Array of SnakeSegment
+var snakeDir = {
+ x: 1,
+ y: 0
+}; // Initial direction: right
+var nextDir = {
+ x: 1,
+ y: 0
+}; // Next direction to turn to
+var food = null;
+var foodPos = {
+ x: 0,
+ y: 0
+};
+var moveTimer = null;
+var isAlive = true;
+var pendingGrowth = 0;
+// --- Score UI ---
+var score = 0;
+var scoreTxt = new Text2('0', {
+ size: 120,
+ fill: 0xFFFFFF
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+// --- Helper Functions ---
+function gridToPos(gx, gy) {
+ // Center the grid in the game area
+ var offsetX = Math.floor((2048 - GRID_WIDTH * GRID_SIZE) / 2);
+ var offsetY = Math.floor((2732 - GRID_HEIGHT * GRID_SIZE) / 2);
+ return {
+ x: offsetX + gx * GRID_SIZE + GRID_SIZE / 2,
+ y: offsetY + gy * GRID_SIZE + GRID_SIZE / 2
+ };
+}
+function posToGrid(x, y) {
+ var offsetX = Math.floor((2048 - GRID_WIDTH * GRID_SIZE) / 2);
+ var offsetY = Math.floor((2732 - GRID_HEIGHT * GRID_SIZE) / 2);
+ return {
+ x: Math.floor((x - offsetX) / GRID_SIZE),
+ y: Math.floor((y - offsetY) / GRID_SIZE)
+ };
+}
+function spawnFood() {
+ // Remove old food if exists
+ if (food) {
+ food.destroy();
+ food = null;
+ }
+ // Find empty cell
+ var emptyCells = [];
+ for (var gx = 0; gx < GRID_WIDTH; gx++) {
+ for (var gy = 0; gy < GRID_HEIGHT; gy++) {
+ var occupied = false;
+ for (var i = 0; i < snake.length; i++) {
+ if (snake[i].gx === gx && snake[i].gy === gy) {
+ occupied = true;
+ break;
+ }
+ }
+ if (!occupied) {
+ emptyCells.push({
+ x: gx,
+ y: gy
+ });
+ }
+ }
+ }
+ if (emptyCells.length === 0) {
+ // No space left, player wins!
+ LK.showYouWin();
+ return;
+ }
+ var idx = Math.floor(Math.random() * emptyCells.length);
+ foodPos = emptyCells[idx];
+ food = new Food();
+ var pos = gridToPos(foodPos.x, foodPos.y);
+ food.x = pos.x;
+ food.y = pos.y;
+ food.gx = foodPos.x;
+ food.gy = foodPos.y;
+ game.addChild(food);
+}
+// --- Snake Initialization ---
+function resetGame() {
+ // Remove old snake
+ for (var i = 0; i < snake.length; i++) {
+ snake[i].destroy();
+ }
+ snake = [];
+ // Remove food
+ if (food) {
+ food.destroy();
+ food = null;
+ }
+ // Reset state
+ isAlive = true;
+ score = 0;
+ scoreTxt.setText(score);
+ snakeDir = {
+ x: 1,
+ y: 0
+ };
+ nextDir = {
+ x: 1,
+ y: 0
+ };
+ pendingGrowth = 2; // Start with 3 segments
+ // Place snake in center
+ var startX = Math.floor(GRID_WIDTH / 2);
+ var startY = Math.floor(GRID_HEIGHT / 2);
+ for (var i = 0; i < 3; i++) {
+ var seg = new SnakeSegment();
+ seg.gx = startX - i;
+ seg.gy = startY;
+ var pos = gridToPos(seg.gx, seg.gy);
+ seg.x = pos.x;
+ seg.y = pos.y;
+ game.addChild(seg);
+ snake.push(seg);
+ }
+ spawnFood();
+ // Start move timer
+ if (moveTimer) LK.clearInterval(moveTimer);
+ moveTimer = LK.setInterval(moveSnake, MOVE_INTERVAL);
+}
+// --- Snake Movement ---
+function moveSnake() {
+ if (!isAlive) return;
+ // Update direction
+ if ((nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y // Prevent 180 turns
+ ) && (nextDir.x !== snakeDir.x || nextDir.y !== snakeDir.y)) {
+ snakeDir.x = nextDir.x;
+ snakeDir.y = nextDir.y;
+ }
+ // Calculate new head position
+ var head = snake[0];
+ var newGX = head.gx + snakeDir.x;
+ var newGY = head.gy + snakeDir.y;
+ // Check wall collision
+ if (newGX < 0 || newGX >= GRID_WIDTH || newGY < 0 || newGY >= GRID_HEIGHT) {
+ gameOver();
+ return;
+ }
+ // Check self collision
+ for (var i = 0; i < snake.length; i++) {
+ if (snake[i].gx === newGX && snake[i].gy === newGY) {
+ gameOver();
+ return;
+ }
+ }
+ // Move segments
+ var prevGX = head.gx;
+ var prevGY = head.gy;
+ var prevPos = gridToPos(prevGX, prevGY);
+ // Move head
+ head.gx = newGX;
+ head.gy = newGY;
+ var newPos = gridToPos(newGX, newGY);
+ tween(head, {
+ x: newPos.x,
+ y: newPos.y
+ }, {
+ duration: MOVE_INTERVAL - 10,
+ easing: tween.linear
+ });
+ // Move body
+ for (var i = 1; i < snake.length; i++) {
+ var seg = snake[i];
+ var tmpGX = seg.gx;
+ var tmpGY = seg.gy;
+ var tmpPos = gridToPos(tmpGX, tmpGY);
+ seg.gx = prevGX;
+ seg.gy = prevGY;
+ tween(seg, {
+ x: prevPos.x,
+ y: prevPos.y
+ }, {
+ duration: MOVE_INTERVAL - 10,
+ easing: tween.linear
+ });
+ prevGX = tmpGX;
+ prevGY = tmpGY;
+ prevPos = tmpPos;
+ }
+ // Growth
+ if (pendingGrowth > 0) {
+ var tail = snake[snake.length - 1];
+ var newSeg = new SnakeSegment();
+ newSeg.gx = prevGX;
+ newSeg.gy = prevGY;
+ var tailPos = gridToPos(prevGX, prevGY);
+ newSeg.x = tailPos.x;
+ newSeg.y = tailPos.y;
+ game.addChild(newSeg);
+ snake.push(newSeg);
+ pendingGrowth--;
+ }
+ // Check food collision
+ if (head.gx === food.gx && head.gy === food.gy) {
+ score++;
+ scoreTxt.setText(score);
+ pendingGrowth++;
+ spawnFood();
+ }
+}
+// --- Game Over ---
+function gameOver() {
+ isAlive = false;
+ LK.effects.flashScreen(0xff0000, 800);
+ if (moveTimer) LK.clearInterval(moveTimer);
+ LK.setTimeout(function () {
+ LK.showGameOver();
+ }, 800);
+}
+// --- Input Handling ---
+// Touch/drag/swipe: change direction
+var dragStart = null;
+var dragLast = null;
+var dragThreshold = 40; // px
+function handleDirectionInput(x, y) {
+ // Convert to grid
+ var head = snake[0];
+ var pos = gridToPos(head.gx, head.gy);
+ var dx = x - pos.x;
+ var dy = y - pos.y;
+ if (Math.abs(dx) > Math.abs(dy)) {
+ // Horizontal
+ if (dx > dragThreshold && snakeDir.x !== -1) {
+ nextDir = {
+ x: 1,
+ y: 0
+ };
+ } else if (dx < -dragThreshold && snakeDir.x !== 1) {
+ nextDir = {
+ x: -1,
+ y: 0
+ };
+ }
+ } else {
+ // Vertical
+ if (dy > dragThreshold && snakeDir.y !== -1) {
+ nextDir = {
+ x: 0,
+ y: 1
+ };
+ } else if (dy < -dragThreshold && snakeDir.y !== 1) {
+ nextDir = {
+ x: 0,
+ y: -1
+ };
+ }
+ }
+}
+// Touch down: start drag
+game.down = function (x, y, obj) {
+ dragStart = {
+ x: x,
+ y: y
+ };
+ dragLast = {
+ x: x,
+ y: y
+ };
+};
+// Touch move: detect swipe
+game.move = function (x, y, obj) {
+ if (!dragStart) return;
+ var dx = x - dragStart.x;
+ var dy = y - dragStart.y;
+ if (Math.abs(dx) > dragThreshold || Math.abs(dy) > dragThreshold) {
+ handleDirectionInput(x, y);
+ dragStart = {
+ x: x,
+ y: y
+ }; // Reset for next swipe
+ }
+ dragLast = {
+ x: x,
+ y: y
+ };
+};
+// Touch up: tap to turn (if no swipe)
+game.up = function (x, y, obj) {
+ if (!dragStart) return;
+ var dx = x - dragStart.x;
+ var dy = y - dragStart.y;
+ if (Math.abs(dx) < dragThreshold && Math.abs(dy) < dragThreshold) {
+ // Tap: turn clockwise
+ if (snakeDir.x === 1 && snakeDir.y === 0) nextDir = {
+ x: 0,
+ y: 1
+ };else if (snakeDir.x === 0 && snakeDir.y === 1) nextDir = {
+ x: -1,
+ y: 0
+ };else if (snakeDir.x === -1 && snakeDir.y === 0) nextDir = {
+ x: 0,
+ y: -1
+ };else if (snakeDir.x === 0 && snakeDir.y === -1) nextDir = {
+ x: 1,
+ y: 0
+ };
+ }
+ dragStart = null;
+ dragLast = null;
+};
+// --- Game Update (not used for movement, but could be used for future features) ---
+game.update = function () {
+ // No per-frame logic needed for MVP
+};
+// --- Start Game ---
+resetGame();
\ No newline at end of file
ultra realistic apple. In-Game asset. 2d. High contrast. No shadows
create ultra realistic pear. In-Game asset. 2d. High contrast. No shadows
ultra realistic strawberry. In-Game asset. 2d. High contrast. No shadows
ultra realistic orange fruit. In-Game asset. 2d. High contrast. No shadows
snake head 16x16. In-Game asset. 2d. High contrast. No shadows