/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Single cell in the grid var Cell = Container.expand(function () { var self = Container.call(this); // Attach a square asset for the cell var cellAsset = self.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); self.isAlive = false; self.gridX = 0; self.gridY = 0; // Set cell state (alive/dead) self.setAlive = function (alive) { self.isAlive = alive; cellAsset.color = alive ? 0x4caf50 : 0x222222; cellAsset.alpha = alive ? 1 : 0.25; }; // Flash cell when it changes state self.flash = function () { tween(cellAsset, { alpha: 1 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(cellAsset, { alpha: self.isAlive ? 1 : 0.25 }, { duration: 180 }); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Grid settings var GRID_COLS = 16; var GRID_ROWS = 20; var CELL_SIZE = 96; // 2048/16 = 128, but use 96 for padding var GRID_WIDTH = GRID_COLS * CELL_SIZE; var GRID_HEIGHT = GRID_ROWS * CELL_SIZE; var GRID_OFFSET_X = (2048 - GRID_WIDTH) / 2; var GRID_OFFSET_Y = (2732 - GRID_HEIGHT) / 2 + 60; // 2D array of cells var grid = []; var cellNodes = []; // Used to track drawing state var isDrawing = false; var drawState = true; // true = drawing alive, false = erasing // Create cell asset (square) // Create grid of cells for (var y = 0; y < GRID_ROWS; y++) { var row = []; var nodeRow = []; for (var x = 0; x < GRID_COLS; x++) { var cell = new Cell(); cell.gridX = x; cell.gridY = y; cell.x = GRID_OFFSET_X + x * CELL_SIZE + CELL_SIZE / 2; cell.y = GRID_OFFSET_Y + y * CELL_SIZE + CELL_SIZE / 2; cell.setAlive(false); game.addChild(cell); row.push(0); nodeRow.push(cell); } grid.push(row); cellNodes.push(nodeRow); } // Draw instructions var instructions = new Text2("Draw: Tap cells\nEvolve: Press ▶", { size: 70, fill: 0xFFFFFF }); instructions.anchor.set(0.5, 0); LK.gui.top.addChild(instructions); // Add evolve/play button var playBtn = new Text2("▶", { size: 120, fill: 0x4CAF50 }); playBtn.anchor.set(0.5, 0.5); playBtn.x = 0; playBtn.y = 0; LK.gui.bottom.addChild(playBtn); // Add reset button var resetBtn = new Text2("⟳", { size: 90, fill: 0xFFFFFF }); resetBtn.anchor.set(0.5, 0.5); resetBtn.x = -180; resetBtn.y = 0; LK.gui.bottom.addChild(resetBtn); // Add step button var stepBtn = new Text2("⏭", { size: 90, fill: 0xFFFFFF }); stepBtn.anchor.set(0.5, 0.5); stepBtn.x = 180; stepBtn.y = 0; LK.gui.bottom.addChild(stepBtn); // Only one mode: Variant (original rules) instructions.setText("Draw: Tap cells\nEvolve: Press ▶"); // State var isRunning = false; var evolveTimer = null; // Helper: get alive neighbor count function countAliveNeighbors(gx, gy) { 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 = gx + dx; var ny = gy + dy; if (nx >= 0 && nx < GRID_COLS && ny >= 0 && ny < GRID_ROWS) { if (cellNodes[ny][nx].isAlive) count++; } } } return count; } // Evolve grid by one step function evolveGrid() { var toLive = []; var toDie = []; var toRegen = []; var changed = false; for (var y = 0; y < GRID_ROWS; y++) { for (var x = 0; x < GRID_COLS; x++) { var cell = cellNodes[y][x]; var alive = cell.isAlive; var neighbors = countAliveNeighbors(x, y); // Only one mode: original rules if (alive) { if (neighbors >= 5) { toDie.push(cell); changed = true; } } else { if (neighbors === 9) { toRegen.push(cell); changed = true; } } } } // If no cell would change, force a change: flip a random cell if (!changed) { var allCells = []; for (var y = 0; y < GRID_ROWS; y++) { for (var x = 0; x < GRID_COLS; x++) { allCells.push(cellNodes[y][x]); } } if (allCells.length > 0) { var idx = Math.floor(Math.random() * allCells.length); var cell = allCells[idx]; if (cell.isAlive) { toDie.push(cell); } else { toRegen.push(cell); } } changed = true; } // Apply changes for (var i = 0; i < toDie.length; i++) { toDie[i].setAlive(false); toDie[i].flash(); } for (var i = 0; i < toRegen.length; i++) { toRegen[i].setAlive(true); toRegen[i].flash(); } } // Start/stop evolution function startEvolve() { if (isRunning) return; isRunning = true; instructions.setText("Watch your pattern evolve!"); evolveTimer = LK.setInterval(function () { evolveGrid(); }, 400); } function stopEvolve() { if (!isRunning) return; isRunning = false; instructions.setText("Draw: Tap cells\nEvolve: Press ▶"); if (evolveTimer) { LK.clearInterval(evolveTimer); evolveTimer = null; } } // Reset grid function resetGrid() { stopEvolve(); for (var y = 0; y < GRID_ROWS; y++) { for (var x = 0; x < GRID_COLS; x++) { cellNodes[y][x].setAlive(false); } } } // Step evolution once function stepEvolve() { if (isRunning) return; evolveGrid(); } // Find cell at game coordinates function getCellAt(x, y) { for (var row = 0; row < GRID_ROWS; row++) { for (var col = 0; col < GRID_COLS; col++) { var cell = cellNodes[row][col]; var left = cell.x - CELL_SIZE / 2; var right = cell.x + CELL_SIZE / 2; var top = cell.y - CELL_SIZE / 2; var bottom = cell.y + CELL_SIZE / 2; if (x >= left && x < right && y >= top && y < bottom) { return cell; } } } return null; } // Handle drawing on grid game.down = function (x, y, obj) { if (isRunning) return; var cell = getCellAt(x, y); if (cell) { isDrawing = true; drawState = !cell.isAlive; cell.setAlive(drawState); cell.flash(); } }; game.move = function (x, y, obj) { if (isRunning) return; if (!isDrawing) return; var cell = getCellAt(x, y); if (cell && cell.isAlive !== drawState) { cell.setAlive(drawState); cell.flash(); } }; game.up = function (x, y, obj) { isDrawing = false; }; // Play button events playBtn.down = function (x, y, obj) { if (isRunning) { stopEvolve(); playBtn.setText("▶"); } else { startEvolve(); playBtn.setText("⏸"); } }; resetBtn.down = function (x, y, obj) { resetGrid(); playBtn.setText("▶"); }; stepBtn.down = function (x, y, obj) { stepEvolve(); }; // Prevent accidental grid drawing when using GUI LK.gui.bottom.down = function (x, y, obj) {}; LK.gui.bottom.move = function (x, y, obj) {}; LK.gui.bottom.up = function (x, y, obj) {}; // No update loop needed for this game game.update = function () { // No-op };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Single cell in the grid
var Cell = Container.expand(function () {
var self = Container.call(this);
// Attach a square asset for the cell
var cellAsset = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5
});
self.isAlive = false;
self.gridX = 0;
self.gridY = 0;
// Set cell state (alive/dead)
self.setAlive = function (alive) {
self.isAlive = alive;
cellAsset.color = alive ? 0x4caf50 : 0x222222;
cellAsset.alpha = alive ? 1 : 0.25;
};
// Flash cell when it changes state
self.flash = function () {
tween(cellAsset, {
alpha: 1
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(cellAsset, {
alpha: self.isAlive ? 1 : 0.25
}, {
duration: 180
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Grid settings
var GRID_COLS = 16;
var GRID_ROWS = 20;
var CELL_SIZE = 96; // 2048/16 = 128, but use 96 for padding
var GRID_WIDTH = GRID_COLS * CELL_SIZE;
var GRID_HEIGHT = GRID_ROWS * CELL_SIZE;
var GRID_OFFSET_X = (2048 - GRID_WIDTH) / 2;
var GRID_OFFSET_Y = (2732 - GRID_HEIGHT) / 2 + 60;
// 2D array of cells
var grid = [];
var cellNodes = [];
// Used to track drawing state
var isDrawing = false;
var drawState = true; // true = drawing alive, false = erasing
// Create cell asset (square)
// Create grid of cells
for (var y = 0; y < GRID_ROWS; y++) {
var row = [];
var nodeRow = [];
for (var x = 0; x < GRID_COLS; x++) {
var cell = new Cell();
cell.gridX = x;
cell.gridY = y;
cell.x = GRID_OFFSET_X + x * CELL_SIZE + CELL_SIZE / 2;
cell.y = GRID_OFFSET_Y + y * CELL_SIZE + CELL_SIZE / 2;
cell.setAlive(false);
game.addChild(cell);
row.push(0);
nodeRow.push(cell);
}
grid.push(row);
cellNodes.push(nodeRow);
}
// Draw instructions
var instructions = new Text2("Draw: Tap cells\nEvolve: Press ▶", {
size: 70,
fill: 0xFFFFFF
});
instructions.anchor.set(0.5, 0);
LK.gui.top.addChild(instructions);
// Add evolve/play button
var playBtn = new Text2("▶", {
size: 120,
fill: 0x4CAF50
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = 0;
playBtn.y = 0;
LK.gui.bottom.addChild(playBtn);
// Add reset button
var resetBtn = new Text2("⟳", {
size: 90,
fill: 0xFFFFFF
});
resetBtn.anchor.set(0.5, 0.5);
resetBtn.x = -180;
resetBtn.y = 0;
LK.gui.bottom.addChild(resetBtn);
// Add step button
var stepBtn = new Text2("⏭", {
size: 90,
fill: 0xFFFFFF
});
stepBtn.anchor.set(0.5, 0.5);
stepBtn.x = 180;
stepBtn.y = 0;
LK.gui.bottom.addChild(stepBtn);
// Only one mode: Variant (original rules)
instructions.setText("Draw: Tap cells\nEvolve: Press ▶");
// State
var isRunning = false;
var evolveTimer = null;
// Helper: get alive neighbor count
function countAliveNeighbors(gx, gy) {
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 = gx + dx;
var ny = gy + dy;
if (nx >= 0 && nx < GRID_COLS && ny >= 0 && ny < GRID_ROWS) {
if (cellNodes[ny][nx].isAlive) count++;
}
}
}
return count;
}
// Evolve grid by one step
function evolveGrid() {
var toLive = [];
var toDie = [];
var toRegen = [];
var changed = false;
for (var y = 0; y < GRID_ROWS; y++) {
for (var x = 0; x < GRID_COLS; x++) {
var cell = cellNodes[y][x];
var alive = cell.isAlive;
var neighbors = countAliveNeighbors(x, y);
// Only one mode: original rules
if (alive) {
if (neighbors >= 5) {
toDie.push(cell);
changed = true;
}
} else {
if (neighbors === 9) {
toRegen.push(cell);
changed = true;
}
}
}
}
// If no cell would change, force a change: flip a random cell
if (!changed) {
var allCells = [];
for (var y = 0; y < GRID_ROWS; y++) {
for (var x = 0; x < GRID_COLS; x++) {
allCells.push(cellNodes[y][x]);
}
}
if (allCells.length > 0) {
var idx = Math.floor(Math.random() * allCells.length);
var cell = allCells[idx];
if (cell.isAlive) {
toDie.push(cell);
} else {
toRegen.push(cell);
}
}
changed = true;
}
// Apply changes
for (var i = 0; i < toDie.length; i++) {
toDie[i].setAlive(false);
toDie[i].flash();
}
for (var i = 0; i < toRegen.length; i++) {
toRegen[i].setAlive(true);
toRegen[i].flash();
}
}
// Start/stop evolution
function startEvolve() {
if (isRunning) return;
isRunning = true;
instructions.setText("Watch your pattern evolve!");
evolveTimer = LK.setInterval(function () {
evolveGrid();
}, 400);
}
function stopEvolve() {
if (!isRunning) return;
isRunning = false;
instructions.setText("Draw: Tap cells\nEvolve: Press ▶");
if (evolveTimer) {
LK.clearInterval(evolveTimer);
evolveTimer = null;
}
}
// Reset grid
function resetGrid() {
stopEvolve();
for (var y = 0; y < GRID_ROWS; y++) {
for (var x = 0; x < GRID_COLS; x++) {
cellNodes[y][x].setAlive(false);
}
}
}
// Step evolution once
function stepEvolve() {
if (isRunning) return;
evolveGrid();
}
// Find cell at game coordinates
function getCellAt(x, y) {
for (var row = 0; row < GRID_ROWS; row++) {
for (var col = 0; col < GRID_COLS; col++) {
var cell = cellNodes[row][col];
var left = cell.x - CELL_SIZE / 2;
var right = cell.x + CELL_SIZE / 2;
var top = cell.y - CELL_SIZE / 2;
var bottom = cell.y + CELL_SIZE / 2;
if (x >= left && x < right && y >= top && y < bottom) {
return cell;
}
}
}
return null;
}
// Handle drawing on grid
game.down = function (x, y, obj) {
if (isRunning) return;
var cell = getCellAt(x, y);
if (cell) {
isDrawing = true;
drawState = !cell.isAlive;
cell.setAlive(drawState);
cell.flash();
}
};
game.move = function (x, y, obj) {
if (isRunning) return;
if (!isDrawing) return;
var cell = getCellAt(x, y);
if (cell && cell.isAlive !== drawState) {
cell.setAlive(drawState);
cell.flash();
}
};
game.up = function (x, y, obj) {
isDrawing = false;
};
// Play button events
playBtn.down = function (x, y, obj) {
if (isRunning) {
stopEvolve();
playBtn.setText("▶");
} else {
startEvolve();
playBtn.setText("⏸");
}
};
resetBtn.down = function (x, y, obj) {
resetGrid();
playBtn.setText("▶");
};
stepBtn.down = function (x, y, obj) {
stepEvolve();
};
// Prevent accidental grid drawing when using GUI
LK.gui.bottom.down = function (x, y, obj) {};
LK.gui.bottom.move = function (x, y, obj) {};
LK.gui.bottom.up = function (x, y, obj) {};
// No update loop needed for this game
game.update = function () {
// No-op
};