User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var localPos = block.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 330
Code edit (1 edits merged)
Please save this source code
User prompt
Mine Escape
Initial prompt
💣 4. Mine Escape Concept: You’re trapped in a grid with hidden bombs. Click to reveal safe blocks. Goal: Clear all safe blocks without hitting bombs (like Minesweeper). Tools: Python (tkinter) Bonus idea: Add random power-ups or hint system.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var GridBlock = Container.expand(function (gridX, gridY, hasMine) {
var self = Container.call(this);
self.gridX = gridX;
self.gridY = gridY;
self.hasMine = hasMine;
self.isRevealed = false;
self.isFlagged = false;
self.adjacentMines = 0;
// Visual elements
self.hiddenGraphics = self.attachAsset('hiddenBlock', {
anchorX: 0.5,
anchorY: 0.5
});
self.revealedGraphics = self.attachAsset('revealedBlock', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.mineGraphics = self.attachAsset('mineBlock', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.flagGraphics = self.attachAsset('flaggedBlock', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Number text for adjacent mine count
self.numberText = new Text2('', {
size: 80,
fill: 0x000000
});
self.numberText.anchor.set(0.5, 0.5);
self.numberText.alpha = 0;
self.addChild(self.numberText);
self.reveal = function () {
if (self.isRevealed || self.isFlagged) return;
self.isRevealed = true;
LK.getSound('reveal').play();
// Hide hidden block
tween(self.hiddenGraphics, {
alpha: 0
}, {
duration: 200
});
if (self.hasMine) {
// Show mine
tween(self.mineGraphics, {
alpha: 1
}, {
duration: 200
});
LK.getSound('explode').play();
gameOver = true;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
} else {
// Show revealed block
tween(self.revealedGraphics, {
alpha: 1
}, {
duration: 200
});
// Show number if adjacent mines > 0
if (self.adjacentMines > 0) {
self.numberText.setText(self.adjacentMines.toString());
self.numberText.fill = self.getNumberColor(self.adjacentMines);
tween(self.numberText, {
alpha: 1
}, {
duration: 200
});
}
}
};
self.toggleFlag = function () {
if (self.isRevealed) return;
self.isFlagged = !self.isFlagged;
LK.getSound('flag').play();
if (self.isFlagged) {
tween(self.flagGraphics, {
alpha: 1
}, {
duration: 200
});
tween(self.hiddenGraphics, {
alpha: 0.5
}, {
duration: 200
});
} else {
tween(self.flagGraphics, {
alpha: 0
}, {
duration: 200
});
tween(self.hiddenGraphics, {
alpha: 1
}, {
duration: 200
});
}
};
self.getNumberColor = function (num) {
var colors = ['#000000', '#0000ff', '#008000', '#ff0000', '#800080', '#800000', '#008080', '#000000', '#808080'];
return colors[num] || '#000000';
};
self.down = function (x, y, obj) {
if (gameOver) return;
// Simple tap reveals, long press flags (simulated with double tap)
if (self.lastTapTime && Date.now() - self.lastTapTime < 300) {
self.toggleFlag();
} else {
if (!self.isFlagged) {
self.reveal();
}
}
self.lastTapTime = Date.now();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c5530
});
/****
* Game Code
****/
var GRID_SIZE = 8;
var MINE_COUNT = 10;
var BLOCK_SIZE = 200;
var BLOCK_SPACING = 220;
var gameGrid = [];
var blocks = [];
var gameOver = false;
var revealedCount = 0;
var totalSafeBlocks = GRID_SIZE * GRID_SIZE - MINE_COUNT;
// UI Elements
var titleText = new Text2('Mine Escape', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
LK.gui.top.addChild(titleText);
var instructionText = new Text2('Tap to reveal • Double tap to flag', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 1);
instructionText.y = -50;
LK.gui.bottom.addChild(instructionText);
var mineCountText = new Text2('Mines: ' + MINE_COUNT, {
size: 80,
fill: 0xFFFFFF
});
mineCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(mineCountText);
mineCountText.x = 120; // Avoid platform menu
function initializeGrid() {
// Create empty grid
for (var y = 0; y < GRID_SIZE; y++) {
gameGrid[y] = [];
for (var x = 0; x < GRID_SIZE; x++) {
gameGrid[y][x] = false; // false = no mine, true = mine
}
}
// Place mines randomly
var minesPlaced = 0;
while (minesPlaced < MINE_COUNT) {
var x = Math.floor(Math.random() * GRID_SIZE);
var y = Math.floor(Math.random() * GRID_SIZE);
if (!gameGrid[y][x]) {
gameGrid[y][x] = true;
minesPlaced++;
}
}
}
function createBlocks() {
var gridWidth = (GRID_SIZE - 1) * BLOCK_SPACING;
var gridHeight = (GRID_SIZE - 1) * BLOCK_SPACING;
var startX = (2048 - gridWidth) / 2;
var startY = (2732 - gridHeight) / 2;
for (var y = 0; y < GRID_SIZE; y++) {
blocks[y] = [];
for (var x = 0; x < GRID_SIZE; x++) {
var block = new GridBlock(x, y, gameGrid[y][x]);
block.x = startX + x * BLOCK_SPACING;
block.y = startY + y * BLOCK_SPACING;
blocks[y][x] = block;
game.addChild(block);
}
}
}
function calculateAdjacentMines() {
for (var y = 0; y < GRID_SIZE; y++) {
for (var x = 0; x < GRID_SIZE; x++) {
if (!blocks[y][x].hasMine) {
var count = 0;
for (var dy = -1; dy <= 1; dy++) {
for (var dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
var nx = x + dx;
var ny = y + dy;
if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) {
if (gameGrid[ny][nx]) count++;
}
}
}
blocks[y][x].adjacentMines = count;
}
}
}
}
function revealAdjacentEmpty(x, y) {
if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return;
var block = blocks[y][x];
if (block.isRevealed || block.hasMine || block.isFlagged) return;
block.isRevealed = true;
revealedCount++;
// Animate reveal
tween(block.hiddenGraphics, {
alpha: 0
}, {
duration: 200
});
tween(block.revealedGraphics, {
alpha: 1
}, {
duration: 200
});
if (block.adjacentMines > 0) {
block.numberText.setText(block.adjacentMines.toString());
block.numberText.fill = block.getNumberColor(block.adjacentMines);
tween(block.numberText, {
alpha: 1
}, {
duration: 200
});
} else {
// If no adjacent mines, reveal surrounding blocks
LK.setTimeout(function () {
for (var dy = -1; dy <= 1; dy++) {
for (var dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
revealAdjacentEmpty(x + dx, y + dy);
}
}
}, 100);
}
}
function checkWinCondition() {
if (revealedCount === totalSafeBlocks) {
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
}
}
// Initialize the game
initializeGrid();
createBlocks();
calculateAdjacentMines();
// Override block reveal to handle flood fill and win condition
game.update = function () {
if (gameOver) return;
// Count revealed safe blocks
var currentRevealed = 0;
for (var y = 0; y < GRID_SIZE; y++) {
for (var x = 0; x < GRID_SIZE; x++) {
var block = blocks[y][x];
if (block.isRevealed && !block.hasMine) {
currentRevealed++;
}
}
}
revealedCount = currentRevealed;
checkWinCondition();
};
// Handle block reveals with flood fill for empty areas
game.down = function (x, y, obj) {
if (gameOver) return;
// Find which block was clicked
for (var by = 0; by < GRID_SIZE; by++) {
for (var bx = 0; bx < GRID_SIZE; bx++) {
var block = blocks[by][bx];
var localPos = block.toLocal(obj.parent.toGlobal(obj.position));
if (Math.abs(localPos.x) < BLOCK_SIZE / 2 && Math.abs(localPos.y) < BLOCK_SIZE / 2) {
if (!block.isRevealed && !block.isFlagged) {
if (block.hasMine) {
block.reveal();
} else {
// Use flood fill for empty areas
revealAdjacentEmpty(bx, by);
}
}
return;
}
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,313 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+var GridBlock = Container.expand(function (gridX, gridY, hasMine) {
+ var self = Container.call(this);
+ self.gridX = gridX;
+ self.gridY = gridY;
+ self.hasMine = hasMine;
+ self.isRevealed = false;
+ self.isFlagged = false;
+ self.adjacentMines = 0;
+ // Visual elements
+ self.hiddenGraphics = self.attachAsset('hiddenBlock', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.revealedGraphics = self.attachAsset('revealedBlock', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0
+ });
+ self.mineGraphics = self.attachAsset('mineBlock', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0
+ });
+ self.flagGraphics = self.attachAsset('flaggedBlock', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0
+ });
+ // Number text for adjacent mine count
+ self.numberText = new Text2('', {
+ size: 80,
+ fill: 0x000000
+ });
+ self.numberText.anchor.set(0.5, 0.5);
+ self.numberText.alpha = 0;
+ self.addChild(self.numberText);
+ self.reveal = function () {
+ if (self.isRevealed || self.isFlagged) return;
+ self.isRevealed = true;
+ LK.getSound('reveal').play();
+ // Hide hidden block
+ tween(self.hiddenGraphics, {
+ alpha: 0
+ }, {
+ duration: 200
+ });
+ if (self.hasMine) {
+ // Show mine
+ tween(self.mineGraphics, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ LK.getSound('explode').play();
+ gameOver = true;
+ LK.setTimeout(function () {
+ LK.showGameOver();
+ }, 500);
+ } else {
+ // Show revealed block
+ tween(self.revealedGraphics, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ // Show number if adjacent mines > 0
+ if (self.adjacentMines > 0) {
+ self.numberText.setText(self.adjacentMines.toString());
+ self.numberText.fill = self.getNumberColor(self.adjacentMines);
+ tween(self.numberText, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ }
+ }
+ };
+ self.toggleFlag = function () {
+ if (self.isRevealed) return;
+ self.isFlagged = !self.isFlagged;
+ LK.getSound('flag').play();
+ if (self.isFlagged) {
+ tween(self.flagGraphics, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ tween(self.hiddenGraphics, {
+ alpha: 0.5
+ }, {
+ duration: 200
+ });
+ } else {
+ tween(self.flagGraphics, {
+ alpha: 0
+ }, {
+ duration: 200
+ });
+ tween(self.hiddenGraphics, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ }
+ };
+ self.getNumberColor = function (num) {
+ var colors = ['#000000', '#0000ff', '#008000', '#ff0000', '#800080', '#800000', '#008080', '#000000', '#808080'];
+ return colors[num] || '#000000';
+ };
+ self.down = function (x, y, obj) {
+ if (gameOver) return;
+ // Simple tap reveals, long press flags (simulated with double tap)
+ if (self.lastTapTime && Date.now() - self.lastTapTime < 300) {
+ self.toggleFlag();
+ } else {
+ if (!self.isFlagged) {
+ self.reveal();
+ }
+ }
+ self.lastTapTime = Date.now();
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x2c5530
+});
+
+/****
+* Game Code
+****/
+var GRID_SIZE = 8;
+var MINE_COUNT = 10;
+var BLOCK_SIZE = 200;
+var BLOCK_SPACING = 220;
+var gameGrid = [];
+var blocks = [];
+var gameOver = false;
+var revealedCount = 0;
+var totalSafeBlocks = GRID_SIZE * GRID_SIZE - MINE_COUNT;
+// UI Elements
+var titleText = new Text2('Mine Escape', {
+ size: 120,
+ fill: 0xFFFFFF
+});
+titleText.anchor.set(0.5, 0);
+LK.gui.top.addChild(titleText);
+var instructionText = new Text2('Tap to reveal • Double tap to flag', {
+ size: 60,
+ fill: 0xFFFFFF
+});
+instructionText.anchor.set(0.5, 1);
+instructionText.y = -50;
+LK.gui.bottom.addChild(instructionText);
+var mineCountText = new Text2('Mines: ' + MINE_COUNT, {
+ size: 80,
+ fill: 0xFFFFFF
+});
+mineCountText.anchor.set(0, 0);
+LK.gui.topLeft.addChild(mineCountText);
+mineCountText.x = 120; // Avoid platform menu
+function initializeGrid() {
+ // Create empty grid
+ for (var y = 0; y < GRID_SIZE; y++) {
+ gameGrid[y] = [];
+ for (var x = 0; x < GRID_SIZE; x++) {
+ gameGrid[y][x] = false; // false = no mine, true = mine
+ }
+ }
+ // Place mines randomly
+ var minesPlaced = 0;
+ while (minesPlaced < MINE_COUNT) {
+ var x = Math.floor(Math.random() * GRID_SIZE);
+ var y = Math.floor(Math.random() * GRID_SIZE);
+ if (!gameGrid[y][x]) {
+ gameGrid[y][x] = true;
+ minesPlaced++;
+ }
+ }
+}
+function createBlocks() {
+ var gridWidth = (GRID_SIZE - 1) * BLOCK_SPACING;
+ var gridHeight = (GRID_SIZE - 1) * BLOCK_SPACING;
+ var startX = (2048 - gridWidth) / 2;
+ var startY = (2732 - gridHeight) / 2;
+ for (var y = 0; y < GRID_SIZE; y++) {
+ blocks[y] = [];
+ for (var x = 0; x < GRID_SIZE; x++) {
+ var block = new GridBlock(x, y, gameGrid[y][x]);
+ block.x = startX + x * BLOCK_SPACING;
+ block.y = startY + y * BLOCK_SPACING;
+ blocks[y][x] = block;
+ game.addChild(block);
+ }
+ }
+}
+function calculateAdjacentMines() {
+ for (var y = 0; y < GRID_SIZE; y++) {
+ for (var x = 0; x < GRID_SIZE; x++) {
+ if (!blocks[y][x].hasMine) {
+ var count = 0;
+ for (var dy = -1; dy <= 1; dy++) {
+ for (var dx = -1; dx <= 1; dx++) {
+ if (dx === 0 && dy === 0) continue;
+ var nx = x + dx;
+ var ny = y + dy;
+ if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) {
+ if (gameGrid[ny][nx]) count++;
+ }
+ }
+ }
+ blocks[y][x].adjacentMines = count;
+ }
+ }
+ }
+}
+function revealAdjacentEmpty(x, y) {
+ if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return;
+ var block = blocks[y][x];
+ if (block.isRevealed || block.hasMine || block.isFlagged) return;
+ block.isRevealed = true;
+ revealedCount++;
+ // Animate reveal
+ tween(block.hiddenGraphics, {
+ alpha: 0
+ }, {
+ duration: 200
+ });
+ tween(block.revealedGraphics, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ if (block.adjacentMines > 0) {
+ block.numberText.setText(block.adjacentMines.toString());
+ block.numberText.fill = block.getNumberColor(block.adjacentMines);
+ tween(block.numberText, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ } else {
+ // If no adjacent mines, reveal surrounding blocks
+ LK.setTimeout(function () {
+ for (var dy = -1; dy <= 1; dy++) {
+ for (var dx = -1; dx <= 1; dx++) {
+ if (dx === 0 && dy === 0) continue;
+ revealAdjacentEmpty(x + dx, y + dy);
+ }
+ }
+ }, 100);
+ }
+}
+function checkWinCondition() {
+ if (revealedCount === totalSafeBlocks) {
+ LK.setTimeout(function () {
+ LK.showYouWin();
+ }, 500);
+ }
+}
+// Initialize the game
+initializeGrid();
+createBlocks();
+calculateAdjacentMines();
+// Override block reveal to handle flood fill and win condition
+game.update = function () {
+ if (gameOver) return;
+ // Count revealed safe blocks
+ var currentRevealed = 0;
+ for (var y = 0; y < GRID_SIZE; y++) {
+ for (var x = 0; x < GRID_SIZE; x++) {
+ var block = blocks[y][x];
+ if (block.isRevealed && !block.hasMine) {
+ currentRevealed++;
+ }
+ }
+ }
+ revealedCount = currentRevealed;
+ checkWinCondition();
+};
+// Handle block reveals with flood fill for empty areas
+game.down = function (x, y, obj) {
+ if (gameOver) return;
+ // Find which block was clicked
+ for (var by = 0; by < GRID_SIZE; by++) {
+ for (var bx = 0; bx < GRID_SIZE; bx++) {
+ var block = blocks[by][bx];
+ var localPos = block.toLocal(obj.parent.toGlobal(obj.position));
+ if (Math.abs(localPos.x) < BLOCK_SIZE / 2 && Math.abs(localPos.y) < BLOCK_SIZE / 2) {
+ if (!block.isRevealed && !block.isFlagged) {
+ if (block.hasMine) {
+ block.reveal();
+ } else {
+ // Use flood fill for empty areas
+ revealAdjacentEmpty(bx, by);
+ }
+ }
+ return;
+ }
+ }
+ }
+};
\ No newline at end of file