/****
* 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
};