/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Cell = Container.expand(function (row, col) { var self = Container.call(this); self.row = row; self.col = col; self.value = 0; // 0 = empty, 1 = X, 2 = O var background = self.attachAsset('cellBackground', { anchorX: 0.5, anchorY: 0.5 }); var symbol = null; self.placeSymbol = function (playerSymbol) { if (self.value !== 0) return false; self.value = playerSymbol; if (playerSymbol === 1) { symbol = self.attachAsset('xSymbol', { anchorX: 0.5, anchorY: 0.5, scaleX: 0, scaleY: 0 }); } else { symbol = self.attachAsset('oSymbol', { anchorX: 0.5, anchorY: 0.5, scaleX: 0, scaleY: 0 }); } tween(symbol, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.elasticOut }); LK.getSound('placeSymbol').play(); return true; }; self.down = function (x, y, obj) { if (gameState === 'playing' && currentPlayer === 1 && self.value === 0) { if (self.placeSymbol(1)) { currentPlayer = 2; checkGameEnd(); if (gameState === 'playing') { LK.setTimeout(function () { aiMove(); }, 500); } } } }; return self; }); var GameBoard = Container.expand(function () { var self = Container.call(this); self.cells = []; self.winLine = null; // Create 3x3 grid for (var row = 0; row < 3; row++) { self.cells[row] = []; for (var col = 0; col < 3; col++) { var cell = new Cell(row, col); cell.x = (col - 1) * 220; cell.y = (row - 1) * 220; self.cells[row][col] = cell; self.addChild(cell); } } // Create grid lines // Vertical lines for (var i = 0; i < 2; i++) { var vLine = self.attachAsset('gridLine', { anchorX: 0.5, anchorY: 0.5, x: (i - 0.5) * 220, y: 0 }); } // Horizontal lines for (var i = 0; i < 2; i++) { var hLine = self.attachAsset('gridLine', { anchorX: 0.5, anchorY: 0.5, x: 0, y: (i - 0.5) * 220, rotation: Math.PI / 2 }); } self.getCell = function (row, col) { return self.cells[row][col]; }; self.getBoardState = function () { var state = []; for (var row = 0; row < 3; row++) { state[row] = []; for (var col = 0; col < 3; col++) { state[row][col] = self.cells[row][col].value; } } return state; }; self.checkWin = function (player) { var board = self.getBoardState(); // Check rows for (var row = 0; row < 3; row++) { if (board[row][0] === player && board[row][1] === player && board[row][2] === player) { return { type: 'row', index: row }; } } // Check columns for (var col = 0; col < 3; col++) { if (board[0][col] === player && board[1][col] === player && board[2][col] === player) { return { type: 'col', index: col }; } } // Check diagonals if (board[0][0] === player && board[1][1] === player && board[2][2] === player) { return { type: 'diag', index: 0 }; } if (board[0][2] === player && board[1][1] === player && board[2][0] === player) { return { type: 'diag', index: 1 }; } return null; }; self.isBoardFull = function () { var board = self.getBoardState(); for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === 0) return false; } } return true; }; self.showWinLine = function (winInfo) { self.winLine = self.attachAsset('winLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 0, alpha: 0.8 }); if (winInfo.type === 'row') { self.winLine.x = 0; self.winLine.y = (winInfo.index - 1) * 220; self.winLine.rotation = 0; } else if (winInfo.type === 'col') { self.winLine.x = (winInfo.index - 1) * 220; self.winLine.y = 0; self.winLine.rotation = Math.PI / 2; } else if (winInfo.type === 'diag') { self.winLine.x = 0; self.winLine.y = 0; if (winInfo.index === 0) { self.winLine.rotation = Math.PI / 4; self.winLine.width = 650; } else { self.winLine.rotation = -Math.PI / 4; self.winLine.width = 650; } } tween(self.winLine, { scaleX: 1 }, { duration: 500, easing: tween.easeOut }); }; self.reset = function () { for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { var cell = self.cells[row][col]; cell.value = 0; if (cell.children.length > 1) { cell.removeChild(cell.children[cell.children.length - 1]); } } } if (self.winLine) { self.removeChild(self.winLine); self.winLine = null; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xF5F5F5 }); /**** * Game Code ****/ var gameBoard = null; var gameState = 'playing'; // 'playing', 'won', 'lost', 'draw' var currentPlayer = 1; // 1 = X (human), 2 = O (AI) // Score tracking var wins = storage.wins || 0; var losses = storage.losses || 0; var draws = storage.draws || 0; // UI Elements var statusText = new Text2('Your Turn (X)', { size: 80, fill: 0x333333 }); statusText.anchor.set(0.5, 0); LK.gui.top.addChild(statusText); statusText.y = 150; var scoreText = new Text2('Wins: ' + wins + ' | Losses: ' + losses + ' | Draws: ' + draws, { size: 50, fill: 0x666666 }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 250; var restartButton = new Text2('Play Again', { size: 60, fill: 0x4CAF50 }); restartButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(restartButton); restartButton.y = -100; restartButton.alpha = 0; // Initialize game board gameBoard = new GameBoard(); gameBoard.x = 2048 / 2; gameBoard.y = 2732 / 2; game.addChild(gameBoard); function checkGameEnd() { var playerWin = gameBoard.checkWin(1); var aiWin = gameBoard.checkWin(2); if (playerWin) { gameState = 'won'; statusText.setText('You Win!'); gameBoard.showWinLine(playerWin); wins++; storage.wins = wins; LK.getSound('gameWin').play(); showRestartButton(); } else if (aiWin) { gameState = 'lost'; statusText.setText('AI Wins!'); gameBoard.showWinLine(aiWin); losses++; storage.losses = losses; LK.getSound('gameLose').play(); showRestartButton(); } else if (gameBoard.isBoardFull()) { gameState = 'draw'; statusText.setText('Draw!'); draws++; storage.draws = draws; LK.getSound('gameDraw').play(); showRestartButton(); } if (gameState !== 'playing') { scoreText.setText('Wins: ' + wins + ' | Losses: ' + losses + ' | Draws: ' + draws); } } function showRestartButton() { tween(restartButton, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); } function minimax(board, depth, isMaximizing) { var aiWin = checkWinOnBoard(board, 2); var playerWin = checkWinOnBoard(board, 1); if (aiWin) return 10 - depth; if (playerWin) return depth - 10; if (isBoardFullArray(board)) return 0; if (isMaximizing) { var bestScore = -1000; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === 0) { board[row][col] = 2; var score = minimax(board, depth + 1, false); board[row][col] = 0; if (score > bestScore) { bestScore = score; } } } } return bestScore; } else { var bestScore = 1000; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === 0) { board[row][col] = 1; var score = minimax(board, depth + 1, true); board[row][col] = 0; if (score < bestScore) { bestScore = score; } } } } return bestScore; } } function checkWinOnBoard(board, player) { // Check rows for (var row = 0; row < 3; row++) { if (board[row][0] === player && board[row][1] === player && board[row][2] === player) { return true; } } // Check columns for (var col = 0; col < 3; col++) { if (board[0][col] === player && board[1][col] === player && board[2][col] === player) { return true; } } // Check diagonals if (board[0][0] === player && board[1][1] === player && board[2][2] === player) { return true; } if (board[0][2] === player && board[1][1] === player && board[2][0] === player) { return true; } return false; } function isBoardFullArray(board) { for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === 0) return false; } } return true; } function aiMove() { if (gameState !== 'playing' || currentPlayer !== 2) return; var board = gameBoard.getBoardState(); var bestScore = -1000; var bestMove = null; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === 0) { board[row][col] = 2; var score = minimax(board, 0, false); board[row][col] = 0; if (score > bestScore) { bestScore = score; bestMove = { row: row, col: col }; } } } } if (bestMove) { var cell = gameBoard.getCell(bestMove.row, bestMove.col); cell.placeSymbol(2); currentPlayer = 1; checkGameEnd(); if (gameState === 'playing') { statusText.setText('Your Turn (X)'); } } } function resetGame() { gameBoard.reset(); gameState = 'playing'; currentPlayer = 1; statusText.setText('Your Turn (X)'); tween(restartButton, { alpha: 0 }, { duration: 300, easing: tween.easeIn }); } restartButton.down = function (x, y, obj) { if (gameState !== 'playing') { resetGame(); } }; game.update = function () { if (gameState === 'playing' && currentPlayer === 1) { statusText.setText('Your Turn (X)'); } else if (gameState === 'playing' && currentPlayer === 2) { statusText.setText('AI Turn (O)'); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cell = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.value = 0; // 0 = empty, 1 = X, 2 = O
var background = self.attachAsset('cellBackground', {
anchorX: 0.5,
anchorY: 0.5
});
var symbol = null;
self.placeSymbol = function (playerSymbol) {
if (self.value !== 0) return false;
self.value = playerSymbol;
if (playerSymbol === 1) {
symbol = self.attachAsset('xSymbol', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
} else {
symbol = self.attachAsset('oSymbol', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
}
tween(symbol, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
LK.getSound('placeSymbol').play();
return true;
};
self.down = function (x, y, obj) {
if (gameState === 'playing' && currentPlayer === 1 && self.value === 0) {
if (self.placeSymbol(1)) {
currentPlayer = 2;
checkGameEnd();
if (gameState === 'playing') {
LK.setTimeout(function () {
aiMove();
}, 500);
}
}
}
};
return self;
});
var GameBoard = Container.expand(function () {
var self = Container.call(this);
self.cells = [];
self.winLine = null;
// Create 3x3 grid
for (var row = 0; row < 3; row++) {
self.cells[row] = [];
for (var col = 0; col < 3; col++) {
var cell = new Cell(row, col);
cell.x = (col - 1) * 220;
cell.y = (row - 1) * 220;
self.cells[row][col] = cell;
self.addChild(cell);
}
}
// Create grid lines
// Vertical lines
for (var i = 0; i < 2; i++) {
var vLine = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: (i - 0.5) * 220,
y: 0
});
}
// Horizontal lines
for (var i = 0; i < 2; i++) {
var hLine = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: (i - 0.5) * 220,
rotation: Math.PI / 2
});
}
self.getCell = function (row, col) {
return self.cells[row][col];
};
self.getBoardState = function () {
var state = [];
for (var row = 0; row < 3; row++) {
state[row] = [];
for (var col = 0; col < 3; col++) {
state[row][col] = self.cells[row][col].value;
}
}
return state;
};
self.checkWin = function (player) {
var board = self.getBoardState();
// Check rows
for (var row = 0; row < 3; row++) {
if (board[row][0] === player && board[row][1] === player && board[row][2] === player) {
return {
type: 'row',
index: row
};
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (board[0][col] === player && board[1][col] === player && board[2][col] === player) {
return {
type: 'col',
index: col
};
}
}
// Check diagonals
if (board[0][0] === player && board[1][1] === player && board[2][2] === player) {
return {
type: 'diag',
index: 0
};
}
if (board[0][2] === player && board[1][1] === player && board[2][0] === player) {
return {
type: 'diag',
index: 1
};
}
return null;
};
self.isBoardFull = function () {
var board = self.getBoardState();
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === 0) return false;
}
}
return true;
};
self.showWinLine = function (winInfo) {
self.winLine = self.attachAsset('winLine', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
alpha: 0.8
});
if (winInfo.type === 'row') {
self.winLine.x = 0;
self.winLine.y = (winInfo.index - 1) * 220;
self.winLine.rotation = 0;
} else if (winInfo.type === 'col') {
self.winLine.x = (winInfo.index - 1) * 220;
self.winLine.y = 0;
self.winLine.rotation = Math.PI / 2;
} else if (winInfo.type === 'diag') {
self.winLine.x = 0;
self.winLine.y = 0;
if (winInfo.index === 0) {
self.winLine.rotation = Math.PI / 4;
self.winLine.width = 650;
} else {
self.winLine.rotation = -Math.PI / 4;
self.winLine.width = 650;
}
}
tween(self.winLine, {
scaleX: 1
}, {
duration: 500,
easing: tween.easeOut
});
};
self.reset = function () {
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var cell = self.cells[row][col];
cell.value = 0;
if (cell.children.length > 1) {
cell.removeChild(cell.children[cell.children.length - 1]);
}
}
}
if (self.winLine) {
self.removeChild(self.winLine);
self.winLine = null;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xF5F5F5
});
/****
* Game Code
****/
var gameBoard = null;
var gameState = 'playing'; // 'playing', 'won', 'lost', 'draw'
var currentPlayer = 1; // 1 = X (human), 2 = O (AI)
// Score tracking
var wins = storage.wins || 0;
var losses = storage.losses || 0;
var draws = storage.draws || 0;
// UI Elements
var statusText = new Text2('Your Turn (X)', {
size: 80,
fill: 0x333333
});
statusText.anchor.set(0.5, 0);
LK.gui.top.addChild(statusText);
statusText.y = 150;
var scoreText = new Text2('Wins: ' + wins + ' | Losses: ' + losses + ' | Draws: ' + draws, {
size: 50,
fill: 0x666666
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 250;
var restartButton = new Text2('Play Again', {
size: 60,
fill: 0x4CAF50
});
restartButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(restartButton);
restartButton.y = -100;
restartButton.alpha = 0;
// Initialize game board
gameBoard = new GameBoard();
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2;
game.addChild(gameBoard);
function checkGameEnd() {
var playerWin = gameBoard.checkWin(1);
var aiWin = gameBoard.checkWin(2);
if (playerWin) {
gameState = 'won';
statusText.setText('You Win!');
gameBoard.showWinLine(playerWin);
wins++;
storage.wins = wins;
LK.getSound('gameWin').play();
showRestartButton();
} else if (aiWin) {
gameState = 'lost';
statusText.setText('AI Wins!');
gameBoard.showWinLine(aiWin);
losses++;
storage.losses = losses;
LK.getSound('gameLose').play();
showRestartButton();
} else if (gameBoard.isBoardFull()) {
gameState = 'draw';
statusText.setText('Draw!');
draws++;
storage.draws = draws;
LK.getSound('gameDraw').play();
showRestartButton();
}
if (gameState !== 'playing') {
scoreText.setText('Wins: ' + wins + ' | Losses: ' + losses + ' | Draws: ' + draws);
}
}
function showRestartButton() {
tween(restartButton, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
}
function minimax(board, depth, isMaximizing) {
var aiWin = checkWinOnBoard(board, 2);
var playerWin = checkWinOnBoard(board, 1);
if (aiWin) return 10 - depth;
if (playerWin) return depth - 10;
if (isBoardFullArray(board)) return 0;
if (isMaximizing) {
var bestScore = -1000;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === 0) {
board[row][col] = 2;
var score = minimax(board, depth + 1, false);
board[row][col] = 0;
if (score > bestScore) {
bestScore = score;
}
}
}
}
return bestScore;
} else {
var bestScore = 1000;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === 0) {
board[row][col] = 1;
var score = minimax(board, depth + 1, true);
board[row][col] = 0;
if (score < bestScore) {
bestScore = score;
}
}
}
}
return bestScore;
}
}
function checkWinOnBoard(board, player) {
// Check rows
for (var row = 0; row < 3; row++) {
if (board[row][0] === player && board[row][1] === player && board[row][2] === player) {
return true;
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (board[0][col] === player && board[1][col] === player && board[2][col] === player) {
return true;
}
}
// Check diagonals
if (board[0][0] === player && board[1][1] === player && board[2][2] === player) {
return true;
}
if (board[0][2] === player && board[1][1] === player && board[2][0] === player) {
return true;
}
return false;
}
function isBoardFullArray(board) {
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === 0) return false;
}
}
return true;
}
function aiMove() {
if (gameState !== 'playing' || currentPlayer !== 2) return;
var board = gameBoard.getBoardState();
var bestScore = -1000;
var bestMove = null;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === 0) {
board[row][col] = 2;
var score = minimax(board, 0, false);
board[row][col] = 0;
if (score > bestScore) {
bestScore = score;
bestMove = {
row: row,
col: col
};
}
}
}
}
if (bestMove) {
var cell = gameBoard.getCell(bestMove.row, bestMove.col);
cell.placeSymbol(2);
currentPlayer = 1;
checkGameEnd();
if (gameState === 'playing') {
statusText.setText('Your Turn (X)');
}
}
}
function resetGame() {
gameBoard.reset();
gameState = 'playing';
currentPlayer = 1;
statusText.setText('Your Turn (X)');
tween(restartButton, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
}
restartButton.down = function (x, y, obj) {
if (gameState !== 'playing') {
resetGame();
}
};
game.update = function () {
if (gameState === 'playing' && currentPlayer === 1) {
statusText.setText('Your Turn (X)');
} else if (gameState === 'playing' && currentPlayer === 2) {
statusText.setText('AI Turn (O)');
}
};