/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
// Candy class: represents a single candy on the board
var Candy = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.type = 0; // 0-6, for 7 types
	self.row = 0;
	self.col = 0;
	self.isMatched = false;
	// Attach asset for candy, anchor center
	self.candyAsset = null;
	// Set candy type and update asset
	self.setType = function (type) {
		self.type = type;
		// Remove previous asset if exists
		if (self.candyAsset) {
			self.removeChild(self.candyAsset);
		}
		// Assign each candy type to a unique asset for visual distinction
		var assetIds = ['Sg1',
		// type 0
		'Sg2',
		// type 1
		'Sg3',
		// type 2
		'Sg4',
		// type 3
		'Sg5',
		// type 4
		'Sg6',
		// type 5
		'Sg7' // type 6
		];
		self.candyAsset = self.attachAsset(assetIds[type], {
			anchorX: 0.5,
			anchorY: 0.5
		});
	};
	// Animate pop (match)
	self.pop = function (_onFinish) {
		tween(self, {
			scaleX: 1.3,
			scaleY: 1.3,
			alpha: 0
		}, {
			duration: 250,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.alpha = 1;
				self.scaleX = 1;
				self.scaleY = 1;
				if (_onFinish) _onFinish();
			}
		});
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x222244
});
/**** 
* Game Code
****/ 
// --- Game Constants ---
var boardCols = 7;
var boardRows = 9;
var candyTypes = 7;
var candySize = 200; // px, will be scaled to fit
var boardPadding = 0; // No padding between candies
var moveLimit = 20;
var targetScore = 1800;
// --- Game State ---
var board = []; // 2D array [row][col] of Candy
var candies = []; // Flat array of all Candy objects
var selectedCandy = null;
var swappingCandy = null;
var isSwapping = false;
var isAnimating = false;
var movesLeft = moveLimit;
var score = 0;
var level = storage.level || 1;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
	size: 90,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var movesTxt = new Text2('Moves: ' + movesLeft, {
	size: 70,
	fill: "#fff"
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 110;
// Best score UI
var bestScore = storage.bestScore || 0;
var bestScoreTxt = new Text2('Best: ' + bestScore, {
	size: 60,
	fill: "#fff"
});
bestScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bestScoreTxt);
bestScoreTxt.y = 190;
// Current score UI (label)
var currentScoreTxt = new Text2('Score', {
	size: 60,
	fill: "#fff"
});
currentScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(currentScoreTxt);
currentScoreTxt.y = 40;
// Add "env games" text in the bottom right corner
var envGamesTxt = new Text2('env games', {
	size: 40,
	fill: "#fff"
});
envGamesTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(envGamesTxt);
// --- Board Positioning ---
// Add Sg_bg asset as a background layer, scaled to cover the game area
var sgBgAsset = LK.getAsset('Sg_bg', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732
});
game.addChild(sgBgAsset);
// Add Bg asset as background, scaled to cover the game area
var bgAsset = LK.getAsset('Bg', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732
});
game.addChild(bgAsset);
var boardWidth = boardCols * candySize;
var boardHeight = boardRows * candySize;
var boardOffsetX = Math.floor((2048 - boardWidth) / 2);
var boardOffsetY = Math.floor((2732 - boardHeight) / 2) + 60;
// --- Helper Functions ---
// Get board position (x, y) for a given row, col
function getBoardPos(row, col) {
	return {
		x: boardOffsetX + col * candySize + candySize / 2,
		y: boardOffsetY + row * candySize + candySize / 2
	};
}
// Get candy at (row, col)
function getCandy(row, col) {
	if (row < 0 || row >= boardRows || col < 0 || col >= boardCols) return null;
	return board[row][col];
}
// Swap two candies in board and update their row/col
function swapCandies(c1, c2) {
	if (!c1 || !c2) {
		// Defensive: do nothing if either candy is null or undefined
		return;
	}
	var r1 = c1.row,
		c1c = c1.col,
		r2 = c2.row,
		c2c = c2.col;
	board[r1][c1c] = c2;
	board[r2][c2c] = c1;
	c1.row = r2;
	c1.col = c2c;
	c2.row = r1;
	c2.col = c1c;
}
// Animate swap between two candies
function animateSwap(c1, c2, _onFinish2) {
	if (!c1 || !c2) {
		// Defensive: do nothing if either candy is null or undefined
		isAnimating = false;
		if (_onFinish2) _onFinish2();
		return;
	}
	isAnimating = true;
	var pos1 = getBoardPos(c1.row, c1.col);
	var pos2 = getBoardPos(c2.row, c2.col);
	tween(c1, {
		x: pos2.x,
		y: pos2.y
	}, {
		duration: 180,
		easing: tween.cubicInOut
	});
	tween(c2, {
		x: pos1.x,
		y: pos1.y
	}, {
		duration: 180,
		easing: tween.cubicInOut,
		onFinish: function onFinish() {
			isAnimating = false;
			if (_onFinish2) _onFinish2();
		}
	});
}
// Animate candy falling to new position
function animateMoveTo(candy, newRow, newCol, onFinish) {
	var pos = getBoardPos(newRow, newCol);
	tween(candy, {
		x: pos.x,
		y: pos.y
	}, {
		duration: 180,
		easing: tween.cubicInOut,
		onFinish: onFinish
	});
}
// Deselect all candies
function deselectAll() {
	if (selectedCandy) {
		tween(selectedCandy, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 100
		});
		selectedCandy = null;
	}
}
// Check if two candies are adjacent
function areAdjacent(c1, c2) {
	return Math.abs(c1.row - c2.row) + Math.abs(c1.col - c2.col) === 1;
}
// Find all matches on the board, mark candies as isMatched
function findMatches() {
	var found = false;
	// Clear previous matches
	for (var r = 0; r < boardRows; r++) {
		for (var c = 0; c < boardCols; c++) {
			if (board[r][c]) board[r][c].isMatched = false;
		}
	}
	// Horizontal matches
	for (var r = 0; r < boardRows; r++) {
		var matchLen = 1;
		for (var c = 1; c < boardCols; c++) {
			if (board[r][c].type === board[r][c - 1].type) {
				matchLen++;
			} else {
				if (matchLen >= 3) {
					for (var k = 0; k < matchLen; k++) {
						board[r][c - 1 - k].isMatched = true;
					}
					found = true;
				}
				matchLen = 1;
			}
		}
		if (matchLen >= 3) {
			for (var k = 0; k < matchLen; k++) {
				board[r][boardCols - 1 - k].isMatched = true;
			}
			found = true;
		}
	}
	// Vertical matches
	for (var c = 0; c < boardCols; c++) {
		var matchLen = 1;
		for (var r = 1; r < boardRows; r++) {
			if (board[r][c].type === board[r - 1][c].type) {
				matchLen++;
			} else {
				if (matchLen >= 3) {
					for (var k = 0; k < matchLen; k++) {
						board[r - 1 - k][c].isMatched = true;
					}
					found = true;
				}
				matchLen = 1;
			}
		}
		if (matchLen >= 3) {
			for (var k = 0; k < matchLen; k++) {
				board[boardRows - 1 - k][c].isMatched = true;
			}
			found = true;
		}
	}
	return found;
}
// Remove matched candies, return number removed
function removeMatches(onFinish) {
	var removed = 0;
	var toPop = [];
	for (var r = 0; r < boardRows; r++) {
		for (var c = 0; c < boardCols; c++) {
			var candy = board[r][c];
			if (candy.isMatched) {
				toPop.push(candy);
				removed++;
			}
		}
	}
	if (removed === 0) {
		if (onFinish) onFinish();
		return;
	}
	var popped = 0;
	for (var i = 0; i < toPop.length; i++) {
		// Play sg_crash sound for each exploding candy
		LK.getSound('Sg_crash').play();
		toPop[i].pop(function () {
			popped++;
			if (popped === toPop.length) {
				for (var j = 0; j < toPop.length; j++) {
					var c = toPop[j];
					game.removeChild(c);
					candies.splice(candies.indexOf(c), 1);
					board[c.row][c.col] = null;
				}
				if (onFinish) onFinish(removed);
			}
		});
	}
}
// Drop candies down to fill empty spaces, return true if any moved
function dropCandies(onFinish) {
	var moved = false;
	var moves = [];
	for (var c = 0; c < boardCols; c++) {
		for (var r = boardRows - 1; r >= 0; r--) {
			if (board[r][c] === null) {
				// Find first non-null above
				for (var k = r - 1; k >= 0; k--) {
					if (board[k][c]) {
						var candy = board[k][c];
						board[r][c] = candy;
						board[k][c] = null;
						var oldRow = candy.row;
						candy.row = r;
						moves.push({
							candy: candy,
							from: oldRow,
							to: r,
							col: c
						});
						break;
					}
				}
			}
		}
	}
	if (moves.length === 0) {
		if (onFinish) onFinish(false);
		return;
	}
	var finished = 0;
	for (var i = 0; i < moves.length; i++) {
		var move = moves[i];
		animateMoveTo(move.candy, move.to, move.col, function () {
			finished++;
			if (finished === moves.length) {
				if (onFinish) onFinish(true);
			}
		});
	}
}
// Fill empty spaces at the top with new candies
function fillBoard(onFinish) {
	var filled = 0;
	var toFill = [];
	for (var c = 0; c < boardCols; c++) {
		for (var r = 0; r < boardRows; r++) {
			if (board[r][c] === null) {
				var candy = new Candy();
				var type = Math.floor(Math.random() * candyTypes);
				candy.setType(type);
				candy.row = r;
				candy.col = c;
				var pos = getBoardPos(r, c);
				candy.x = pos.x;
				candy.y = pos.y - 400; // Drop from above
				game.addChild(candy);
				candies.push(candy);
				board[r][c] = candy;
				toFill.push(candy);
			}
		}
	}
	if (toFill.length === 0) {
		if (onFinish) onFinish();
		return;
	}
	var finished = 0;
	for (var i = 0; i < toFill.length; i++) {
		var candy = toFill[i];
		var pos = getBoardPos(candy.row, candy.col);
		animateMoveTo(candy, candy.row, candy.col, function () {
			finished++;
			if (finished === toFill.length) {
				if (onFinish) onFinish();
			}
		});
	}
}
// Check if the board has any possible moves (for future: shuffle if not)
function hasPossibleMoves() {
	// For MVP, skip shuffle, always assume possible moves
	return true;
}
// Update UI
function updateUI() {
	scoreTxt.setText(score);
	movesTxt.setText('Moves: ' + movesLeft);
	if (score > bestScore) {
		bestScore = score;
		storage.bestScore = bestScore;
	}
	bestScoreTxt.setText('Best: ' + bestScore);
}
// Handle end of turn: check for matches, drop, fill, repeat as needed
function resolveBoard(afterResolve) {
	if (findMatches()) {
		removeMatches(function (removed) {
			// Award more points for more candies exploded at once: 3=100, 4=150, 5=200, 6+=4x points
			var points = 0;
			if (removed >= 3) {
				if (removed === 3) {
					points = 100;
				} else if (removed === 4) {
					points = Math.floor(100 * 1.5); // 150
				} else if (removed === 5) {
					points = 200;
				} else if (removed >= 6) {
					// 6-pack or more: 4x the base points for that many candies
					// Base: 3=100, 4=150, 5=200, 6=250, 7=300, 8=350, etc.
					var base = 100 + Math.max(0, removed - 3) * 50;
					points = base * 4;
				}
			}
			score += points;
			updateUI();
			dropCandies(function () {
				fillBoard(function () {
					resolveBoard(afterResolve);
				});
			});
		});
	} else {
		if (afterResolve) afterResolve();
	}
}
// Handle win/lose
function checkEnd() {
	if (movesLeft <= 0) {
		// Only end the game if there are no matches left to resolve
		if (!findMatches()) {
			// Show game over when moves are finished and board is resolved
			LK.showGameOver();
			return true;
		}
		// Otherwise, let the board resolve naturally before ending
		return false;
	}
	return false;
}
// --- Board Initialization ---
function createBoard() {
	// Clear previous
	for (var i = 0; i < candies.length; i++) {
		game.removeChild(candies[i]);
	}
	candies = [];
	board = [];
	for (var r = 0; r < boardRows; r++) {
		board[r] = [];
		for (var c = 0; c < boardCols; c++) {
			var candy = new Candy();
			var type = Math.floor(Math.random() * candyTypes);
			candy.setType(type);
			candy.row = r;
			candy.col = c;
			var pos = getBoardPos(r, c);
			candy.x = pos.x;
			candy.y = pos.y;
			game.addChild(candy);
			candies.push(candy);
			board[r][c] = candy;
		}
	}
	// Remove any initial matches
	while (findMatches()) {
		for (var r = 0; r < boardRows; r++) {
			for (var c = 0; c < boardCols; c++) {
				if (board[r][c] && board[r][c].isMatched) {
					var newType;
					do {
						newType = Math.floor(Math.random() * candyTypes);
					} while (r > 0 && board[r - 1][c] && board[r - 1][c].type === newType || c > 0 && board[r][c - 1] && board[r][c - 1].type === newType);
					board[r][c].setType(newType);
					board[r][c].isMatched = false;
				}
			}
		}
	}
}
// --- Input Handling ---
// Find candy at (x, y) in game coordinates
function findCandyAt(x, y) {
	for (var i = 0; i < candies.length; i++) {
		var c = candies[i];
		var dx = x - c.x;
		var dy = y - c.y;
		if (Math.abs(dx) < candySize / 2 && Math.abs(dy) < candySize / 2) {
			return c;
		}
	}
	return null;
}
// Handle tap or drag
game.down = function (x, y, obj) {
	if (isAnimating) return;
	var c = findCandyAt(x, y);
	if (!c) {
		deselectAll();
		return;
	}
	if (!selectedCandy) {
		selectedCandy = c;
		tween(selectedCandy, {
			scaleX: 1.15,
			scaleY: 1.15
		}, {
			duration: 100
		});
	} else if (selectedCandy === c) {
		deselectAll();
	} else if (areAdjacent(selectedCandy, c)) {
		// Swap attempt
		isSwapping = true;
		swappingCandy = c;
		// Animate swap
		animateSwap(selectedCandy, swappingCandy, function () {
			swapCandies(selectedCandy, swappingCandy);
			// Check if swap creates a match
			if (findMatches()) {
				movesLeft--;
				updateUI();
				resolveBoard(function () {
					deselectAll();
					isSwapping = false;
					swappingCandy = null;
					checkEnd();
				});
			} else {
				// No match, swap back and decrement moves (wrong move)
				// Play Wrong sound
				LK.getSound('Wrong').play();
				movesLeft--;
				updateUI();
				animateSwap(selectedCandy, swappingCandy, function () {
					swapCandies(selectedCandy, swappingCandy);
					deselectAll();
					isSwapping = false;
					swappingCandy = null;
					checkEnd();
				});
			}
		});
	} else {
		// Select new candy
		tween(selectedCandy, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 100
		});
		selectedCandy = c;
		tween(selectedCandy, {
			scaleX: 1.15,
			scaleY: 1.15
		}, {
			duration: 100
		});
	}
};
// Drag to swap
game.move = function (x, y, obj) {
	if (isAnimating || !selectedCandy) return;
	var c = findCandyAt(x, y);
	if (c && c !== selectedCandy && areAdjacent(selectedCandy, c)) {
		game.down(x, y, obj);
	}
};
game.up = function (x, y, obj) {
	// No-op for now
};
// --- Game Start ---
function startGame() {
	level = storage.level || 1;
	score = 0;
	if (level === 2) {
		movesLeft = 10;
		targetScore = 2500;
	} else {
		movesLeft = moveLimit;
		targetScore = 1800;
	}
	updateUI();
	createBoard();
	deselectAll();
	isAnimating = false;
	isSwapping = false;
	swappingCandy = null;
	selectedCandy = null;
	// Start Fnt music
	LK.playMusic('Fnt');
	// Remove any matches at start
	resolveBoard();
}
startGame();
// --- Game Tick ---
game.update = function () {
	// No per-frame logic needed for MVP
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
// Candy class: represents a single candy on the board
var Candy = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.type = 0; // 0-6, for 7 types
	self.row = 0;
	self.col = 0;
	self.isMatched = false;
	// Attach asset for candy, anchor center
	self.candyAsset = null;
	// Set candy type and update asset
	self.setType = function (type) {
		self.type = type;
		// Remove previous asset if exists
		if (self.candyAsset) {
			self.removeChild(self.candyAsset);
		}
		// Assign each candy type to a unique asset for visual distinction
		var assetIds = ['Sg1',
		// type 0
		'Sg2',
		// type 1
		'Sg3',
		// type 2
		'Sg4',
		// type 3
		'Sg5',
		// type 4
		'Sg6',
		// type 5
		'Sg7' // type 6
		];
		self.candyAsset = self.attachAsset(assetIds[type], {
			anchorX: 0.5,
			anchorY: 0.5
		});
	};
	// Animate pop (match)
	self.pop = function (_onFinish) {
		tween(self, {
			scaleX: 1.3,
			scaleY: 1.3,
			alpha: 0
		}, {
			duration: 250,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.alpha = 1;
				self.scaleX = 1;
				self.scaleY = 1;
				if (_onFinish) _onFinish();
			}
		});
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x222244
});
/**** 
* Game Code
****/ 
// --- Game Constants ---
var boardCols = 7;
var boardRows = 9;
var candyTypes = 7;
var candySize = 200; // px, will be scaled to fit
var boardPadding = 0; // No padding between candies
var moveLimit = 20;
var targetScore = 1800;
// --- Game State ---
var board = []; // 2D array [row][col] of Candy
var candies = []; // Flat array of all Candy objects
var selectedCandy = null;
var swappingCandy = null;
var isSwapping = false;
var isAnimating = false;
var movesLeft = moveLimit;
var score = 0;
var level = storage.level || 1;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
	size: 90,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var movesTxt = new Text2('Moves: ' + movesLeft, {
	size: 70,
	fill: "#fff"
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 110;
// Best score UI
var bestScore = storage.bestScore || 0;
var bestScoreTxt = new Text2('Best: ' + bestScore, {
	size: 60,
	fill: "#fff"
});
bestScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bestScoreTxt);
bestScoreTxt.y = 190;
// Current score UI (label)
var currentScoreTxt = new Text2('Score', {
	size: 60,
	fill: "#fff"
});
currentScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(currentScoreTxt);
currentScoreTxt.y = 40;
// Add "env games" text in the bottom right corner
var envGamesTxt = new Text2('env games', {
	size: 40,
	fill: "#fff"
});
envGamesTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(envGamesTxt);
// --- Board Positioning ---
// Add Sg_bg asset as a background layer, scaled to cover the game area
var sgBgAsset = LK.getAsset('Sg_bg', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732
});
game.addChild(sgBgAsset);
// Add Bg asset as background, scaled to cover the game area
var bgAsset = LK.getAsset('Bg', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732
});
game.addChild(bgAsset);
var boardWidth = boardCols * candySize;
var boardHeight = boardRows * candySize;
var boardOffsetX = Math.floor((2048 - boardWidth) / 2);
var boardOffsetY = Math.floor((2732 - boardHeight) / 2) + 60;
// --- Helper Functions ---
// Get board position (x, y) for a given row, col
function getBoardPos(row, col) {
	return {
		x: boardOffsetX + col * candySize + candySize / 2,
		y: boardOffsetY + row * candySize + candySize / 2
	};
}
// Get candy at (row, col)
function getCandy(row, col) {
	if (row < 0 || row >= boardRows || col < 0 || col >= boardCols) return null;
	return board[row][col];
}
// Swap two candies in board and update their row/col
function swapCandies(c1, c2) {
	if (!c1 || !c2) {
		// Defensive: do nothing if either candy is null or undefined
		return;
	}
	var r1 = c1.row,
		c1c = c1.col,
		r2 = c2.row,
		c2c = c2.col;
	board[r1][c1c] = c2;
	board[r2][c2c] = c1;
	c1.row = r2;
	c1.col = c2c;
	c2.row = r1;
	c2.col = c1c;
}
// Animate swap between two candies
function animateSwap(c1, c2, _onFinish2) {
	if (!c1 || !c2) {
		// Defensive: do nothing if either candy is null or undefined
		isAnimating = false;
		if (_onFinish2) _onFinish2();
		return;
	}
	isAnimating = true;
	var pos1 = getBoardPos(c1.row, c1.col);
	var pos2 = getBoardPos(c2.row, c2.col);
	tween(c1, {
		x: pos2.x,
		y: pos2.y
	}, {
		duration: 180,
		easing: tween.cubicInOut
	});
	tween(c2, {
		x: pos1.x,
		y: pos1.y
	}, {
		duration: 180,
		easing: tween.cubicInOut,
		onFinish: function onFinish() {
			isAnimating = false;
			if (_onFinish2) _onFinish2();
		}
	});
}
// Animate candy falling to new position
function animateMoveTo(candy, newRow, newCol, onFinish) {
	var pos = getBoardPos(newRow, newCol);
	tween(candy, {
		x: pos.x,
		y: pos.y
	}, {
		duration: 180,
		easing: tween.cubicInOut,
		onFinish: onFinish
	});
}
// Deselect all candies
function deselectAll() {
	if (selectedCandy) {
		tween(selectedCandy, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 100
		});
		selectedCandy = null;
	}
}
// Check if two candies are adjacent
function areAdjacent(c1, c2) {
	return Math.abs(c1.row - c2.row) + Math.abs(c1.col - c2.col) === 1;
}
// Find all matches on the board, mark candies as isMatched
function findMatches() {
	var found = false;
	// Clear previous matches
	for (var r = 0; r < boardRows; r++) {
		for (var c = 0; c < boardCols; c++) {
			if (board[r][c]) board[r][c].isMatched = false;
		}
	}
	// Horizontal matches
	for (var r = 0; r < boardRows; r++) {
		var matchLen = 1;
		for (var c = 1; c < boardCols; c++) {
			if (board[r][c].type === board[r][c - 1].type) {
				matchLen++;
			} else {
				if (matchLen >= 3) {
					for (var k = 0; k < matchLen; k++) {
						board[r][c - 1 - k].isMatched = true;
					}
					found = true;
				}
				matchLen = 1;
			}
		}
		if (matchLen >= 3) {
			for (var k = 0; k < matchLen; k++) {
				board[r][boardCols - 1 - k].isMatched = true;
			}
			found = true;
		}
	}
	// Vertical matches
	for (var c = 0; c < boardCols; c++) {
		var matchLen = 1;
		for (var r = 1; r < boardRows; r++) {
			if (board[r][c].type === board[r - 1][c].type) {
				matchLen++;
			} else {
				if (matchLen >= 3) {
					for (var k = 0; k < matchLen; k++) {
						board[r - 1 - k][c].isMatched = true;
					}
					found = true;
				}
				matchLen = 1;
			}
		}
		if (matchLen >= 3) {
			for (var k = 0; k < matchLen; k++) {
				board[boardRows - 1 - k][c].isMatched = true;
			}
			found = true;
		}
	}
	return found;
}
// Remove matched candies, return number removed
function removeMatches(onFinish) {
	var removed = 0;
	var toPop = [];
	for (var r = 0; r < boardRows; r++) {
		for (var c = 0; c < boardCols; c++) {
			var candy = board[r][c];
			if (candy.isMatched) {
				toPop.push(candy);
				removed++;
			}
		}
	}
	if (removed === 0) {
		if (onFinish) onFinish();
		return;
	}
	var popped = 0;
	for (var i = 0; i < toPop.length; i++) {
		// Play sg_crash sound for each exploding candy
		LK.getSound('Sg_crash').play();
		toPop[i].pop(function () {
			popped++;
			if (popped === toPop.length) {
				for (var j = 0; j < toPop.length; j++) {
					var c = toPop[j];
					game.removeChild(c);
					candies.splice(candies.indexOf(c), 1);
					board[c.row][c.col] = null;
				}
				if (onFinish) onFinish(removed);
			}
		});
	}
}
// Drop candies down to fill empty spaces, return true if any moved
function dropCandies(onFinish) {
	var moved = false;
	var moves = [];
	for (var c = 0; c < boardCols; c++) {
		for (var r = boardRows - 1; r >= 0; r--) {
			if (board[r][c] === null) {
				// Find first non-null above
				for (var k = r - 1; k >= 0; k--) {
					if (board[k][c]) {
						var candy = board[k][c];
						board[r][c] = candy;
						board[k][c] = null;
						var oldRow = candy.row;
						candy.row = r;
						moves.push({
							candy: candy,
							from: oldRow,
							to: r,
							col: c
						});
						break;
					}
				}
			}
		}
	}
	if (moves.length === 0) {
		if (onFinish) onFinish(false);
		return;
	}
	var finished = 0;
	for (var i = 0; i < moves.length; i++) {
		var move = moves[i];
		animateMoveTo(move.candy, move.to, move.col, function () {
			finished++;
			if (finished === moves.length) {
				if (onFinish) onFinish(true);
			}
		});
	}
}
// Fill empty spaces at the top with new candies
function fillBoard(onFinish) {
	var filled = 0;
	var toFill = [];
	for (var c = 0; c < boardCols; c++) {
		for (var r = 0; r < boardRows; r++) {
			if (board[r][c] === null) {
				var candy = new Candy();
				var type = Math.floor(Math.random() * candyTypes);
				candy.setType(type);
				candy.row = r;
				candy.col = c;
				var pos = getBoardPos(r, c);
				candy.x = pos.x;
				candy.y = pos.y - 400; // Drop from above
				game.addChild(candy);
				candies.push(candy);
				board[r][c] = candy;
				toFill.push(candy);
			}
		}
	}
	if (toFill.length === 0) {
		if (onFinish) onFinish();
		return;
	}
	var finished = 0;
	for (var i = 0; i < toFill.length; i++) {
		var candy = toFill[i];
		var pos = getBoardPos(candy.row, candy.col);
		animateMoveTo(candy, candy.row, candy.col, function () {
			finished++;
			if (finished === toFill.length) {
				if (onFinish) onFinish();
			}
		});
	}
}
// Check if the board has any possible moves (for future: shuffle if not)
function hasPossibleMoves() {
	// For MVP, skip shuffle, always assume possible moves
	return true;
}
// Update UI
function updateUI() {
	scoreTxt.setText(score);
	movesTxt.setText('Moves: ' + movesLeft);
	if (score > bestScore) {
		bestScore = score;
		storage.bestScore = bestScore;
	}
	bestScoreTxt.setText('Best: ' + bestScore);
}
// Handle end of turn: check for matches, drop, fill, repeat as needed
function resolveBoard(afterResolve) {
	if (findMatches()) {
		removeMatches(function (removed) {
			// Award more points for more candies exploded at once: 3=100, 4=150, 5=200, 6+=4x points
			var points = 0;
			if (removed >= 3) {
				if (removed === 3) {
					points = 100;
				} else if (removed === 4) {
					points = Math.floor(100 * 1.5); // 150
				} else if (removed === 5) {
					points = 200;
				} else if (removed >= 6) {
					// 6-pack or more: 4x the base points for that many candies
					// Base: 3=100, 4=150, 5=200, 6=250, 7=300, 8=350, etc.
					var base = 100 + Math.max(0, removed - 3) * 50;
					points = base * 4;
				}
			}
			score += points;
			updateUI();
			dropCandies(function () {
				fillBoard(function () {
					resolveBoard(afterResolve);
				});
			});
		});
	} else {
		if (afterResolve) afterResolve();
	}
}
// Handle win/lose
function checkEnd() {
	if (movesLeft <= 0) {
		// Only end the game if there are no matches left to resolve
		if (!findMatches()) {
			// Show game over when moves are finished and board is resolved
			LK.showGameOver();
			return true;
		}
		// Otherwise, let the board resolve naturally before ending
		return false;
	}
	return false;
}
// --- Board Initialization ---
function createBoard() {
	// Clear previous
	for (var i = 0; i < candies.length; i++) {
		game.removeChild(candies[i]);
	}
	candies = [];
	board = [];
	for (var r = 0; r < boardRows; r++) {
		board[r] = [];
		for (var c = 0; c < boardCols; c++) {
			var candy = new Candy();
			var type = Math.floor(Math.random() * candyTypes);
			candy.setType(type);
			candy.row = r;
			candy.col = c;
			var pos = getBoardPos(r, c);
			candy.x = pos.x;
			candy.y = pos.y;
			game.addChild(candy);
			candies.push(candy);
			board[r][c] = candy;
		}
	}
	// Remove any initial matches
	while (findMatches()) {
		for (var r = 0; r < boardRows; r++) {
			for (var c = 0; c < boardCols; c++) {
				if (board[r][c] && board[r][c].isMatched) {
					var newType;
					do {
						newType = Math.floor(Math.random() * candyTypes);
					} while (r > 0 && board[r - 1][c] && board[r - 1][c].type === newType || c > 0 && board[r][c - 1] && board[r][c - 1].type === newType);
					board[r][c].setType(newType);
					board[r][c].isMatched = false;
				}
			}
		}
	}
}
// --- Input Handling ---
// Find candy at (x, y) in game coordinates
function findCandyAt(x, y) {
	for (var i = 0; i < candies.length; i++) {
		var c = candies[i];
		var dx = x - c.x;
		var dy = y - c.y;
		if (Math.abs(dx) < candySize / 2 && Math.abs(dy) < candySize / 2) {
			return c;
		}
	}
	return null;
}
// Handle tap or drag
game.down = function (x, y, obj) {
	if (isAnimating) return;
	var c = findCandyAt(x, y);
	if (!c) {
		deselectAll();
		return;
	}
	if (!selectedCandy) {
		selectedCandy = c;
		tween(selectedCandy, {
			scaleX: 1.15,
			scaleY: 1.15
		}, {
			duration: 100
		});
	} else if (selectedCandy === c) {
		deselectAll();
	} else if (areAdjacent(selectedCandy, c)) {
		// Swap attempt
		isSwapping = true;
		swappingCandy = c;
		// Animate swap
		animateSwap(selectedCandy, swappingCandy, function () {
			swapCandies(selectedCandy, swappingCandy);
			// Check if swap creates a match
			if (findMatches()) {
				movesLeft--;
				updateUI();
				resolveBoard(function () {
					deselectAll();
					isSwapping = false;
					swappingCandy = null;
					checkEnd();
				});
			} else {
				// No match, swap back and decrement moves (wrong move)
				// Play Wrong sound
				LK.getSound('Wrong').play();
				movesLeft--;
				updateUI();
				animateSwap(selectedCandy, swappingCandy, function () {
					swapCandies(selectedCandy, swappingCandy);
					deselectAll();
					isSwapping = false;
					swappingCandy = null;
					checkEnd();
				});
			}
		});
	} else {
		// Select new candy
		tween(selectedCandy, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 100
		});
		selectedCandy = c;
		tween(selectedCandy, {
			scaleX: 1.15,
			scaleY: 1.15
		}, {
			duration: 100
		});
	}
};
// Drag to swap
game.move = function (x, y, obj) {
	if (isAnimating || !selectedCandy) return;
	var c = findCandyAt(x, y);
	if (c && c !== selectedCandy && areAdjacent(selectedCandy, c)) {
		game.down(x, y, obj);
	}
};
game.up = function (x, y, obj) {
	// No-op for now
};
// --- Game Start ---
function startGame() {
	level = storage.level || 1;
	score = 0;
	if (level === 2) {
		movesLeft = 10;
		targetScore = 2500;
	} else {
		movesLeft = moveLimit;
		targetScore = 1800;
	}
	updateUI();
	createBoard();
	deselectAll();
	isAnimating = false;
	isSwapping = false;
	swappingCandy = null;
	selectedCandy = null;
	// Start Fnt music
	LK.playMusic('Fnt');
	// Remove any matches at start
	resolveBoard();
}
startGame();
// --- Game Tick ---
game.update = function () {
	// No per-frame logic needed for MVP
};