User prompt
make it 4x4, insider squares will be 2x2
User prompt
4x4 ölç
User prompt
when filled cell clicked do not take any action
User prompt
title and timer are overlapped place them side to side
User prompt
when correct unicorn selected, place it to the cell and remove from the possible options
User prompt
when i selected a empty cell, just show possible unicorns
User prompt
show selectable unicorns at the bottom of sudoku table with some margin
Code edit (1 edits merged)
Please save this source code
User prompt
when empty cell selected, options are not shown fully. please give a space top of the selectable unicorns
User prompt
i just can see purple squares instead of uniorn images
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'var global = boardContainer.toGlobal(cell.position);' Line Number: 392
User prompt
Please fix the bug: 'Uncaught TypeError: cell.setSelected is not a function' in or related to this line: 'cell.setSelected(true);' Line Number: 384
Code edit (1 edits merged)
Please save this source code
User prompt
Unicorn Sudoku
Initial prompt
create a sudoku game but use unicorn character icons instead of numbers
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // UnicornCell: Represents a single cell in the Sudoku grid var UnicornCell = Container.expand(function () { var self = Container.call(this); // Cell background (for selection/highlight) var bg = self.attachAsset('cellBg', { width: cellSize - 8, height: cellSize - 8, color: 0xffffff, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); self.bg = bg; // The unicorn icon (if any) self.icon = null; self.unicornIndex = null; // 0-8, or null if empty // Row, col for logic self.row = 0; self.col = 0; // Is this a given (pre-filled) cell? self.isGiven = false; // Show error highlight self.showError = function (on) { if (on) { tween(self.bg, { color: 0xffaaaa }, { duration: 200, easing: tween.easeIn }); } else { tween(self.bg, { color: 0xffffff }, { duration: 200, easing: tween.easeOut }); } }; // Set unicorn icon self.setUnicorn = function (idx, animate) { if (self.icon) { self.removeChild(self.icon); self.icon = null; } self.unicornIndex = idx; if (idx !== null) { var icon = self.attachAsset('unicorn' + idx, { width: cellSize * 0.7, height: cellSize * 0.7, anchorX: 0.5, anchorY: 0.5 }); self.icon = icon; if (animate) { icon.scaleX = 0.2; icon.scaleY = 0.2; tween(icon, { scaleX: 1, scaleY: 1 }, { duration: 180, easing: tween.bounceOut }); } } }; // Remove unicorn icon self.clearUnicorn = function () { if (self.icon) { self.removeChild(self.icon); self.icon = null; } self.unicornIndex = null; }; // Selection highlight self.setSelected = function (on) { if (on) { tween(self.bg, { color: 0xcce6ff }, { duration: 120 }); } else { tween(self.bg, { color: 0xffffff }, { duration: 120 }); } }; return self; }); // UnicornPicker: The row of unicorn icons for selection var UnicornPicker = Container.expand(function () { var self = Container.call(this); self.icons = []; self.visible = false; self.selectedIdx = null; // Create 9 unicorn icons for (var i = 0; i < 9; i++) { var icon = self.attachAsset('unicorn' + i, { width: pickerIconSize, height: pickerIconSize, anchorX: 0.5, anchorY: 0.5, x: i * (pickerIconSize + pickerGap), y: 0 }); icon.idx = i; self.icons.push(icon); } // Background var pickerBg = self.attachAsset('pickerBg', { width: 9 * pickerIconSize + 8 * pickerGap + 32, height: pickerIconSize + 32, color: 0xf7e6ff, shape: 'box', anchorX: 0, anchorY: 0 }); pickerBg.x = -16; pickerBg.y = -16; self.setChildIndex(pickerBg, 0); // Highlight selected self.setSelected = function (idx) { for (var i = 0; i < self.icons.length; i++) { self.icons[i].alpha = i === idx ? 1 : 0.7; self.icons[i].scaleX = self.icons[i].scaleY = i === idx ? 1.15 : 1; } self.selectedIdx = idx; }; // Show/hide self.show = function (x, y) { self.x = x; self.y = y; self.visible = true; }; self.hide = function () { self.visible = false; }; return self; }); /**** * Initialize Game ****/ /**** * Game Constants ****/ // Board layout // --- Game Initialization --- var game = new LK.Game({ backgroundColor: 0xf7e6ff }); /**** * Game Code ****/ // Board layout /**** * Game Constants ****/ // --- Game State --- var gridSize = 9; var cellSize = 180; // 9*180 = 1620, fits well in 2048 width var boardSize = cellSize * gridSize; var boardOffsetX = (2048 - boardSize) / 2; var boardOffsetY = 220; // Picker var pickerIconSize = 120; var pickerGap = 18; // For error highlight var errorFlashDuration = 400; // --- Asset Initialization --- // 9 unicorn icons for (var i = 0; i < 9; i++) {} // Cell background // Picker background var board = []; // 2D array of UnicornCell var givenBoard = []; // 2D array of unicorn indices (0-8) or null var solutionBoard = []; // 2D array of unicorn indices (0-8) var selectedCell = null; var picker = null; var timerTxt = null; var startTime = null; var timerInterval = null; var errorCells = []; var completed = false; // --- Sudoku Puzzle Generation --- // For MVP, use a static puzzle and solution (easy level) var staticPuzzle = [[null, null, 2, null, 3, null, 7, null, null], [6, null, null, 1, 9, 5, null, null, null], [null, 9, 8, null, null, null, null, 6, null], [8, null, null, null, 6, null, null, null, 3], [4, null, null, 8, null, 3, null, null, 1], [7, null, null, null, 2, null, null, null, 6], [null, 6, null, null, null, null, 2, 8, null], [null, null, null, 4, 1, 9, null, null, 5], [null, null, 5, null, 8, null, null, 7, 9]]; // Solution for the above puzzle (0-8 for unicorn indices) var staticSolution = [[0, 1, 2, 6, 3, 8, 7, 5, 4], [6, 7, 4, 1, 9, 5, 8, 2, 3], [5, 9, 8, 7, 4, 2, 1, 6, 0], [8, 5, 1, 2, 6, 7, 4, 0, 3], [4, 2, 6, 8, 5, 3, 9, 7, 1], [7, 3, 9, 5, 2, 4, 0, 8, 6], [1, 6, 3, 9, 7, 0, 5, 4, 2], [2, 8, 7, 4, 1, 9, 6, 3, 5], [9, 4, 5, 3, 8, 6, 2, 1, 7]]; // --- Helper Functions --- // Returns true if placing unicornIdx at (row, col) is valid function isValidMove(row, col, unicornIdx) { // Check row for (var c = 0; c < gridSize; c++) { if (c !== col && board[row][c].unicornIndex === unicornIdx) return false; } // Check col for (var r = 0; r < gridSize; r++) { if (r !== row && board[r][col].unicornIndex === unicornIdx) return false; } // Check 3x3 box var boxRow = Math.floor(row / 3) * 3; var boxCol = Math.floor(col / 3) * 3; for (var r = boxRow; r < boxRow + 3; r++) { for (var c = boxCol; c < boxCol + 3; c++) { if ((r !== row || c !== col) && board[r][c].unicornIndex === unicornIdx) return false; } } return true; } // Returns true if the board is completely and correctly filled function isBoardComplete() { for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { var cell = board[r][c]; if (cell.unicornIndex === null) return false; if (!isValidMove(r, c, cell.unicornIndex)) return false; } } return true; } // Highlight errors for current board state function highlightErrors() { // Clear previous for (var i = 0; i < errorCells.length; i++) { errorCells[i].showError(false); } errorCells = []; // Check for errors for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { var cell = board[r][c]; if (cell.unicornIndex === null) continue; // Only highlight if not a given if (!cell.isGiven && !isValidMove(r, c, cell.unicornIndex)) { cell.showError(true); errorCells.push(cell); } } } } // Deselect all cells function deselectAllCells() { for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { board[r][c].setSelected(false); } } selectedCell = null; } // --- UI Setup --- // Board container var boardContainer = new Container(); boardContainer.x = boardOffsetX; boardContainer.y = boardOffsetY; game.addChild(boardContainer); // Draw grid lines (thick for boxes, thin for cells) for (var i = 0; i <= gridSize; i++) { // Vertical var lineW = i % 3 === 0 ? 8 : 3; var lineColor = i % 3 === 0 ? 0x9b59b6 : 0xd1b3e0; var vLine = LK.getAsset('vLine' + i, { width: lineW, height: boardSize, color: lineColor, shape: 'box', anchorX: 0.5, anchorY: 0 }); vLine.x = i * cellSize; vLine.y = 0; boardContainer.addChild(vLine); // Horizontal var hLine = LK.getAsset('hLine' + i, { width: boardSize, height: lineW, color: lineColor, shape: 'box', anchorX: 0, anchorY: 0.5 }); hLine.x = 0; hLine.y = i * cellSize; boardContainer.addChild(hLine); } // Create cells for (var r = 0; r < gridSize; r++) { board[r] = []; for (var c = 0; c < gridSize; c++) { var cell = new UnicornCell(); cell.x = c * cellSize + cellSize / 2; cell.y = r * cellSize + cellSize / 2; cell.row = r; cell.col = c; boardContainer.addChild(cell); board[r][c] = cell; } } // Fill in given cells and solution for (var r = 0; r < gridSize; r++) { givenBoard[r] = []; solutionBoard[r] = []; for (var c = 0; c < gridSize; c++) { var idx = staticPuzzle[r][c]; var cell = board[r][c]; if (idx !== null) { cell.setUnicorn(idx, false); cell.isGiven = true; cell.bg.alpha = 0.7; } givenBoard[r][c] = idx; solutionBoard[r][c] = staticSolution[r][c]; } } // --- Picker UI --- picker = new UnicornPicker(); picker.visible = false; game.addChild(picker); // --- Timer UI --- timerTxt = new Text2('00:00', { size: 90, fill: 0x7D3C98 }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); // --- Title UI --- var titleTxt = new Text2('Unicorn Sudoku', { size: 110, fill: 0xA569BD }); titleTxt.anchor.set(0.5, 0); titleTxt.y = 10; LK.gui.top.addChild(titleTxt); // --- Instructions UI --- var instrTxt = new Text2('Tap a cell, then pick a unicorn to fill it!', { size: 60, fill: 0x7D3C98 }); instrTxt.anchor.set(0.5, 0); instrTxt.y = 120; LK.gui.top.addChild(instrTxt); // --- Event Handlers --- // Cell tap: select cell and show picker function onCellDown(x, y, obj) { if (completed) return; var cell = obj; if (cell.isGiven) return; deselectAllCells(); cell.setSelected(true); selectedCell = cell; // Show picker below the cell, but keep inside screen var global = boardContainer.toGlobal(cell.position); var pickerX = Math.max(40, Math.min(global.x - pickerIconSize * 4, 2048 - pickerIconSize * 9 - 40)); var pickerY = Math.min(global.y + cellSize / 2 + 30, 2732 - pickerIconSize - 80); picker.show(pickerX, pickerY); picker.setSelected(null); } // Picker tap: set unicorn in selected cell function onPickerDown(x, y, obj) { if (!selectedCell || completed) return; var idx = obj.idx; if (idx === undefined) return; // If already has this unicorn, clear if (selectedCell.unicornIndex === idx) { selectedCell.clearUnicorn(); picker.setSelected(null); } else { selectedCell.setUnicorn(idx, true); picker.setSelected(idx); } highlightErrors(); // Check for win if (isBoardComplete()) { completed = true; picker.hide(); deselectAllCells(); LK.effects.flashScreen(0x7d3c98, 800); LK.setTimeout(function () { LK.showYouWin(); }, 900); } } // Hide picker if tap outside function onGameDown(x, y, obj) { // If tap is not on a cell or picker, hide picker and deselect if (obj && (obj instanceof UnicornCell || obj === picker || picker.icons.indexOf(obj) !== -1)) return; picker.hide(); deselectAllCells(); } // --- Attach event handlers --- // Attach to each cell for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { var cell = board[r][c]; cell.down = onCellDown; } } // Attach to picker icons for (var i = 0; i < picker.icons.length; i++) { picker.icons[i].down = onPickerDown; } // Attach to game for outside tap game.down = onGameDown; // --- Timer Logic --- function formatTime(secs) { var m = Math.floor(secs / 60); var s = secs % 60; return (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s; } function updateTimer() { if (completed) return; var now = Date.now(); var elapsed = Math.floor((now - startTime) / 1000); timerTxt.setText(formatTime(elapsed)); } // --- Game Start --- function startGame() { completed = false; deselectAllCells(); picker.hide(); highlightErrors(); startTime = Date.now(); timerTxt.setText('00:00'); if (timerInterval) LK.clearInterval(timerInterval); timerInterval = LK.setInterval(updateTimer, 1000); } startGame(); // --- Game Update (not used for logic, but required) --- game.update = function () { // No per-frame logic needed for MVP };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,444 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// UnicornCell: Represents a single cell in the Sudoku grid
+var UnicornCell = Container.expand(function () {
+ var self = Container.call(this);
+ // Cell background (for selection/highlight)
+ var bg = self.attachAsset('cellBg', {
+ width: cellSize - 8,
+ height: cellSize - 8,
+ color: 0xffffff,
+ shape: 'box',
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.bg = bg;
+ // The unicorn icon (if any)
+ self.icon = null;
+ self.unicornIndex = null; // 0-8, or null if empty
+ // Row, col for logic
+ self.row = 0;
+ self.col = 0;
+ // Is this a given (pre-filled) cell?
+ self.isGiven = false;
+ // Show error highlight
+ self.showError = function (on) {
+ if (on) {
+ tween(self.bg, {
+ color: 0xffaaaa
+ }, {
+ duration: 200,
+ easing: tween.easeIn
+ });
+ } else {
+ tween(self.bg, {
+ color: 0xffffff
+ }, {
+ duration: 200,
+ easing: tween.easeOut
+ });
+ }
+ };
+ // Set unicorn icon
+ self.setUnicorn = function (idx, animate) {
+ if (self.icon) {
+ self.removeChild(self.icon);
+ self.icon = null;
+ }
+ self.unicornIndex = idx;
+ if (idx !== null) {
+ var icon = self.attachAsset('unicorn' + idx, {
+ width: cellSize * 0.7,
+ height: cellSize * 0.7,
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.icon = icon;
+ if (animate) {
+ icon.scaleX = 0.2;
+ icon.scaleY = 0.2;
+ tween(icon, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 180,
+ easing: tween.bounceOut
+ });
+ }
+ }
+ };
+ // Remove unicorn icon
+ self.clearUnicorn = function () {
+ if (self.icon) {
+ self.removeChild(self.icon);
+ self.icon = null;
+ }
+ self.unicornIndex = null;
+ };
+ // Selection highlight
+ self.setSelected = function (on) {
+ if (on) {
+ tween(self.bg, {
+ color: 0xcce6ff
+ }, {
+ duration: 120
+ });
+ } else {
+ tween(self.bg, {
+ color: 0xffffff
+ }, {
+ duration: 120
+ });
+ }
+ };
+ return self;
+});
+// UnicornPicker: The row of unicorn icons for selection
+var UnicornPicker = Container.expand(function () {
+ var self = Container.call(this);
+ self.icons = [];
+ self.visible = false;
+ self.selectedIdx = null;
+ // Create 9 unicorn icons
+ for (var i = 0; i < 9; i++) {
+ var icon = self.attachAsset('unicorn' + i, {
+ width: pickerIconSize,
+ height: pickerIconSize,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: i * (pickerIconSize + pickerGap),
+ y: 0
+ });
+ icon.idx = i;
+ self.icons.push(icon);
+ }
+ // Background
+ var pickerBg = self.attachAsset('pickerBg', {
+ width: 9 * pickerIconSize + 8 * pickerGap + 32,
+ height: pickerIconSize + 32,
+ color: 0xf7e6ff,
+ shape: 'box',
+ anchorX: 0,
+ anchorY: 0
+ });
+ pickerBg.x = -16;
+ pickerBg.y = -16;
+ self.setChildIndex(pickerBg, 0);
+ // Highlight selected
+ self.setSelected = function (idx) {
+ for (var i = 0; i < self.icons.length; i++) {
+ self.icons[i].alpha = i === idx ? 1 : 0.7;
+ self.icons[i].scaleX = self.icons[i].scaleY = i === idx ? 1.15 : 1;
+ }
+ self.selectedIdx = idx;
+ };
+ // Show/hide
+ self.show = function (x, y) {
+ self.x = x;
+ self.y = y;
+ self.visible = true;
+ };
+ self.hide = function () {
+ self.visible = false;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
+/****
+* Game Constants
+****/
+// Board layout
+// --- Game Initialization ---
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0xf7e6ff
+});
+
+/****
+* Game Code
+****/
+// Board layout
+/****
+* Game Constants
+****/
+// --- Game State ---
+var gridSize = 9;
+var cellSize = 180; // 9*180 = 1620, fits well in 2048 width
+var boardSize = cellSize * gridSize;
+var boardOffsetX = (2048 - boardSize) / 2;
+var boardOffsetY = 220;
+// Picker
+var pickerIconSize = 120;
+var pickerGap = 18;
+// For error highlight
+var errorFlashDuration = 400;
+// --- Asset Initialization ---
+// 9 unicorn icons
+for (var i = 0; i < 9; i++) {}
+// Cell background
+// Picker background
+var board = []; // 2D array of UnicornCell
+var givenBoard = []; // 2D array of unicorn indices (0-8) or null
+var solutionBoard = []; // 2D array of unicorn indices (0-8)
+var selectedCell = null;
+var picker = null;
+var timerTxt = null;
+var startTime = null;
+var timerInterval = null;
+var errorCells = [];
+var completed = false;
+// --- Sudoku Puzzle Generation ---
+// For MVP, use a static puzzle and solution (easy level)
+var staticPuzzle = [[null, null, 2, null, 3, null, 7, null, null], [6, null, null, 1, 9, 5, null, null, null], [null, 9, 8, null, null, null, null, 6, null], [8, null, null, null, 6, null, null, null, 3], [4, null, null, 8, null, 3, null, null, 1], [7, null, null, null, 2, null, null, null, 6], [null, 6, null, null, null, null, 2, 8, null], [null, null, null, 4, 1, 9, null, null, 5], [null, null, 5, null, 8, null, null, 7, 9]];
+// Solution for the above puzzle (0-8 for unicorn indices)
+var staticSolution = [[0, 1, 2, 6, 3, 8, 7, 5, 4], [6, 7, 4, 1, 9, 5, 8, 2, 3], [5, 9, 8, 7, 4, 2, 1, 6, 0], [8, 5, 1, 2, 6, 7, 4, 0, 3], [4, 2, 6, 8, 5, 3, 9, 7, 1], [7, 3, 9, 5, 2, 4, 0, 8, 6], [1, 6, 3, 9, 7, 0, 5, 4, 2], [2, 8, 7, 4, 1, 9, 6, 3, 5], [9, 4, 5, 3, 8, 6, 2, 1, 7]];
+// --- Helper Functions ---
+// Returns true if placing unicornIdx at (row, col) is valid
+function isValidMove(row, col, unicornIdx) {
+ // Check row
+ for (var c = 0; c < gridSize; c++) {
+ if (c !== col && board[row][c].unicornIndex === unicornIdx) return false;
+ }
+ // Check col
+ for (var r = 0; r < gridSize; r++) {
+ if (r !== row && board[r][col].unicornIndex === unicornIdx) return false;
+ }
+ // Check 3x3 box
+ var boxRow = Math.floor(row / 3) * 3;
+ var boxCol = Math.floor(col / 3) * 3;
+ for (var r = boxRow; r < boxRow + 3; r++) {
+ for (var c = boxCol; c < boxCol + 3; c++) {
+ if ((r !== row || c !== col) && board[r][c].unicornIndex === unicornIdx) return false;
+ }
+ }
+ return true;
+}
+// Returns true if the board is completely and correctly filled
+function isBoardComplete() {
+ for (var r = 0; r < gridSize; r++) {
+ for (var c = 0; c < gridSize; c++) {
+ var cell = board[r][c];
+ if (cell.unicornIndex === null) return false;
+ if (!isValidMove(r, c, cell.unicornIndex)) return false;
+ }
+ }
+ return true;
+}
+// Highlight errors for current board state
+function highlightErrors() {
+ // Clear previous
+ for (var i = 0; i < errorCells.length; i++) {
+ errorCells[i].showError(false);
+ }
+ errorCells = [];
+ // Check for errors
+ for (var r = 0; r < gridSize; r++) {
+ for (var c = 0; c < gridSize; c++) {
+ var cell = board[r][c];
+ if (cell.unicornIndex === null) continue;
+ // Only highlight if not a given
+ if (!cell.isGiven && !isValidMove(r, c, cell.unicornIndex)) {
+ cell.showError(true);
+ errorCells.push(cell);
+ }
+ }
+ }
+}
+// Deselect all cells
+function deselectAllCells() {
+ for (var r = 0; r < gridSize; r++) {
+ for (var c = 0; c < gridSize; c++) {
+ board[r][c].setSelected(false);
+ }
+ }
+ selectedCell = null;
+}
+// --- UI Setup ---
+// Board container
+var boardContainer = new Container();
+boardContainer.x = boardOffsetX;
+boardContainer.y = boardOffsetY;
+game.addChild(boardContainer);
+// Draw grid lines (thick for boxes, thin for cells)
+for (var i = 0; i <= gridSize; i++) {
+ // Vertical
+ var lineW = i % 3 === 0 ? 8 : 3;
+ var lineColor = i % 3 === 0 ? 0x9b59b6 : 0xd1b3e0;
+ var vLine = LK.getAsset('vLine' + i, {
+ width: lineW,
+ height: boardSize,
+ color: lineColor,
+ shape: 'box',
+ anchorX: 0.5,
+ anchorY: 0
+ });
+ vLine.x = i * cellSize;
+ vLine.y = 0;
+ boardContainer.addChild(vLine);
+ // Horizontal
+ var hLine = LK.getAsset('hLine' + i, {
+ width: boardSize,
+ height: lineW,
+ color: lineColor,
+ shape: 'box',
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ hLine.x = 0;
+ hLine.y = i * cellSize;
+ boardContainer.addChild(hLine);
+}
+// Create cells
+for (var r = 0; r < gridSize; r++) {
+ board[r] = [];
+ for (var c = 0; c < gridSize; c++) {
+ var cell = new UnicornCell();
+ cell.x = c * cellSize + cellSize / 2;
+ cell.y = r * cellSize + cellSize / 2;
+ cell.row = r;
+ cell.col = c;
+ boardContainer.addChild(cell);
+ board[r][c] = cell;
+ }
+}
+// Fill in given cells and solution
+for (var r = 0; r < gridSize; r++) {
+ givenBoard[r] = [];
+ solutionBoard[r] = [];
+ for (var c = 0; c < gridSize; c++) {
+ var idx = staticPuzzle[r][c];
+ var cell = board[r][c];
+ if (idx !== null) {
+ cell.setUnicorn(idx, false);
+ cell.isGiven = true;
+ cell.bg.alpha = 0.7;
+ }
+ givenBoard[r][c] = idx;
+ solutionBoard[r][c] = staticSolution[r][c];
+ }
+}
+// --- Picker UI ---
+picker = new UnicornPicker();
+picker.visible = false;
+game.addChild(picker);
+// --- Timer UI ---
+timerTxt = new Text2('00:00', {
+ size: 90,
+ fill: 0x7D3C98
+});
+timerTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(timerTxt);
+// --- Title UI ---
+var titleTxt = new Text2('Unicorn Sudoku', {
+ size: 110,
+ fill: 0xA569BD
+});
+titleTxt.anchor.set(0.5, 0);
+titleTxt.y = 10;
+LK.gui.top.addChild(titleTxt);
+// --- Instructions UI ---
+var instrTxt = new Text2('Tap a cell, then pick a unicorn to fill it!', {
+ size: 60,
+ fill: 0x7D3C98
+});
+instrTxt.anchor.set(0.5, 0);
+instrTxt.y = 120;
+LK.gui.top.addChild(instrTxt);
+// --- Event Handlers ---
+// Cell tap: select cell and show picker
+function onCellDown(x, y, obj) {
+ if (completed) return;
+ var cell = obj;
+ if (cell.isGiven) return;
+ deselectAllCells();
+ cell.setSelected(true);
+ selectedCell = cell;
+ // Show picker below the cell, but keep inside screen
+ var global = boardContainer.toGlobal(cell.position);
+ var pickerX = Math.max(40, Math.min(global.x - pickerIconSize * 4, 2048 - pickerIconSize * 9 - 40));
+ var pickerY = Math.min(global.y + cellSize / 2 + 30, 2732 - pickerIconSize - 80);
+ picker.show(pickerX, pickerY);
+ picker.setSelected(null);
+}
+// Picker tap: set unicorn in selected cell
+function onPickerDown(x, y, obj) {
+ if (!selectedCell || completed) return;
+ var idx = obj.idx;
+ if (idx === undefined) return;
+ // If already has this unicorn, clear
+ if (selectedCell.unicornIndex === idx) {
+ selectedCell.clearUnicorn();
+ picker.setSelected(null);
+ } else {
+ selectedCell.setUnicorn(idx, true);
+ picker.setSelected(idx);
+ }
+ highlightErrors();
+ // Check for win
+ if (isBoardComplete()) {
+ completed = true;
+ picker.hide();
+ deselectAllCells();
+ LK.effects.flashScreen(0x7d3c98, 800);
+ LK.setTimeout(function () {
+ LK.showYouWin();
+ }, 900);
+ }
+}
+// Hide picker if tap outside
+function onGameDown(x, y, obj) {
+ // If tap is not on a cell or picker, hide picker and deselect
+ if (obj && (obj instanceof UnicornCell || obj === picker || picker.icons.indexOf(obj) !== -1)) return;
+ picker.hide();
+ deselectAllCells();
+}
+// --- Attach event handlers ---
+// Attach to each cell
+for (var r = 0; r < gridSize; r++) {
+ for (var c = 0; c < gridSize; c++) {
+ var cell = board[r][c];
+ cell.down = onCellDown;
+ }
+}
+// Attach to picker icons
+for (var i = 0; i < picker.icons.length; i++) {
+ picker.icons[i].down = onPickerDown;
+}
+// Attach to game for outside tap
+game.down = onGameDown;
+// --- Timer Logic ---
+function formatTime(secs) {
+ var m = Math.floor(secs / 60);
+ var s = secs % 60;
+ return (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s;
+}
+function updateTimer() {
+ if (completed) return;
+ var now = Date.now();
+ var elapsed = Math.floor((now - startTime) / 1000);
+ timerTxt.setText(formatTime(elapsed));
+}
+// --- Game Start ---
+function startGame() {
+ completed = false;
+ deselectAllCells();
+ picker.hide();
+ highlightErrors();
+ startTime = Date.now();
+ timerTxt.setText('00:00');
+ if (timerInterval) LK.clearInterval(timerInterval);
+ timerInterval = LK.setInterval(updateTimer, 1000);
+}
+startGame();
+// --- Game Update (not used for logic, but required) ---
+game.update = function () {
+ // No per-frame logic needed for MVP
+};
\ No newline at end of file