/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var GridCell = Container.expand(function (row, col) { var self = Container.call(this); var cellBg = self.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5 }); self.row = row; self.col = col; self.symbol = null; self.symbolGraphic = null; self.down = function (x, y, obj) { if (gameState === 'playing' && self.symbol === null && !waitingForAI) { self.placeSymbol(playerSymbol); LK.getSound('placeSymbol').play(); if (checkWin(gameBoard, playerSymbol)) { gameState = 'playerWin'; showGameResult('You Win!'); LK.getSound('gameWin').play(); return; } if (checkDraw(gameBoard)) { gameState = 'draw'; showGameResult('Draw!'); LK.getSound('gameDraw').play(); return; } waitingForAI = true; LK.setTimeout(function () { makeAIMove(); waitingForAI = false; }, 500); } }; self.placeSymbol = function (symbol) { if (self.symbol !== null) { return; } self.symbol = symbol; gameBoard[self.row][self.col] = symbol; var assetName = symbol === 'X' ? 'xSymbol' : 'oSymbol'; self.symbolGraphic = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1 }); tween(self.symbolGraphic, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.bounceOut }); }; return self; }); var MenuButton = Container.expand(function (text, difficulty) { var self = Container.call(this); var buttonBg = self.attachAsset('buttonBackground', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(text, { size: 60, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.difficulty = difficulty; self.isHovered = false; self.down = function (x, y, obj) { tween(buttonBg, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); currentDifficulty = self.difficulty; gameState = 'playing'; initializeGame(); }; self.up = function (x, y, obj) { tween(buttonBg, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; return self; }); var TutorialOverlay = Container.expand(function (message) { var self = Container.call(this); var overlay = self.attachAsset('tutorialOverlay', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); var messageText = new Text2(message, { size: 80, fill: 0xFFFFFF }); messageText.anchor.set(0.5, 0.5); self.addChild(messageText); self.down = function (x, y, obj) { self.destroy(); tutorialStep++; showNextTutorial(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ var gameState = 'menu'; // menu, playing, playerWin, aiWin, draw var currentDifficulty = 'EASY'; var gameBoard = []; var gridCells = []; var playerSymbol = 'X'; var aiSymbol = 'O'; var waitingForAI = false; var tutorialStep = 0; var tutorialOverlay = null; // Menu elements var menuButtons = []; var gameGrid = null; var resultText = null; var playAgainButton = null; // Tutorial messages var tutorialMessages = ["Welcome to Tic-Tac-Toe!\nTap to continue...", "Goal: Get 3 symbols in a row\n(horizontal, vertical, or diagonal)", "You are X, AI is O\nYou go first - tap any cell!", "Watch how the AI responds\nto your moves!", "Good luck! Tap to start playing..."]; function initializeMenu() { gameState = 'menu'; // Clear existing elements if (gameGrid) { gameGrid.destroy(); gameGrid = null; } if (resultText) { resultText.destroy(); resultText = null; } if (playAgainButton) { playAgainButton.destroy(); playAgainButton = null; } // Create menu buttons var difficulties = ['BEGINNER', 'EASY', 'MEDIUM', 'HARD']; var buttonSpacing = 200; var startY = 1366 - difficulties.length * buttonSpacing / 2; for (var i = 0; i < difficulties.length; i++) { var button = new MenuButton(difficulties[i], difficulties[i]); button.x = 1024; button.y = startY + i * buttonSpacing; menuButtons.push(button); game.addChild(button); } // Title var titleText = new Text2('Tic-Tac-Toe Master', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; game.addChild(titleText); } function initializeGame() { // Clear menu for (var i = 0; i < menuButtons.length; i++) { menuButtons[i].destroy(); } menuButtons = []; // Random symbol assignment if (Math.random() < 0.5) { playerSymbol = 'X'; aiSymbol = 'O'; } else { playerSymbol = 'O'; aiSymbol = 'X'; } // Initialize game board gameBoard = [[null, null, null], [null, null, null], [null, null, null]]; // Create grid createGrid(); // Show tutorial for beginner mode if (currentDifficulty === 'BEGINNER') { tutorialStep = 0; showNextTutorial(); } } function createGrid() { gameGrid = new Container(); game.addChild(gameGrid); // Center the grid gameGrid.x = 1024; gameGrid.y = 1366; // Create grid background var gridBg = gameGrid.attachAsset('gridBackground', { anchorX: 0.5, anchorY: 0.5 }); // Create cells gridCells = []; for (var row = 0; row < 3; row++) { gridCells[row] = []; for (var col = 0; col < 3; col++) { var cell = new GridCell(row, col); cell.x = (col - 1) * 200; cell.y = (row - 1) * 200; gridCells[row][col] = cell; gameGrid.addChild(cell); } } } function showNextTutorial() { if (currentDifficulty !== 'BEGINNER' || tutorialStep >= tutorialMessages.length) { return; } tutorialOverlay = new TutorialOverlay(tutorialMessages[tutorialStep]); tutorialOverlay.x = 1024; tutorialOverlay.y = 1366; game.addChild(tutorialOverlay); } function makeAIMove() { var availableMoves = []; // Find available moves for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (gameBoard[row][col] === null) { availableMoves.push({ row: row, col: col }); } } } if (availableMoves.length === 0) { return; } var move = null; switch (currentDifficulty) { case 'BEGINNER': case 'EASY': // Random move move = availableMoves[Math.floor(Math.random() * availableMoves.length)]; break; case 'MEDIUM': // Try to win, then block, then random move = findWinningMove(gameBoard, aiSymbol) || findWinningMove(gameBoard, playerSymbol) || availableMoves[Math.floor(Math.random() * availableMoves.length)]; break; case 'HARD': // Strategic AI move = getBestMove(gameBoard, aiSymbol); break; } if (move) { gridCells[move.row][move.col].placeSymbol(aiSymbol); if (checkWin(gameBoard, aiSymbol)) { gameState = 'aiWin'; showGameResult('AI Wins!'); LK.getSound('gameLose').play(); } else if (checkDraw(gameBoard)) { gameState = 'draw'; showGameResult('Draw!'); LK.getSound('gameDraw').play(); } } } function findWinningMove(board, symbol) { for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === null) { board[row][col] = symbol; if (checkWin(board, symbol)) { board[row][col] = null; return { row: row, col: col }; } board[row][col] = null; } } } return null; } function getBestMove(board, symbol) { var bestScore = -Infinity; var bestMove = null; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === null) { board[row][col] = symbol; var score = minimax(board, 0, false, symbol); board[row][col] = null; if (score > bestScore) { bestScore = score; bestMove = { row: row, col: col }; } } } } return bestMove; } function minimax(board, depth, isMaximizing, aiSym) { var playerSym = aiSym === 'X' ? 'O' : 'X'; if (checkWin(board, aiSym)) { return 10 - depth; } if (checkWin(board, playerSym)) { return depth - 10; } if (checkDraw(board)) { return 0; } if (isMaximizing) { var bestScore = -Infinity; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === null) { board[row][col] = aiSym; var score = minimax(board, depth + 1, false, aiSym); board[row][col] = null; bestScore = Math.max(score, bestScore); } } } return bestScore; } else { var bestScore = Infinity; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === null) { board[row][col] = playerSym; var score = minimax(board, depth + 1, true, aiSym); board[row][col] = null; bestScore = Math.min(score, bestScore); } } } return bestScore; } } function checkWin(board, symbol) { // Check rows for (var row = 0; row < 3; row++) { if (board[row][0] === symbol && board[row][1] === symbol && board[row][2] === symbol) { return true; } } // Check columns for (var col = 0; col < 3; col++) { if (board[0][col] === symbol && board[1][col] === symbol && board[2][col] === symbol) { return true; } } // Check diagonals if (board[0][0] === symbol && board[1][1] === symbol && board[2][2] === symbol) { return true; } if (board[0][2] === symbol && board[1][1] === symbol && board[2][0] === symbol) { return true; } return false; } function checkDraw(board) { for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (board[row][col] === null) { return false; } } } return true; } function showGameResult(message) { resultText = new Text2(message, { size: 100, fill: 0xFFFFFF }); resultText.anchor.set(0.5, 0.5); resultText.x = 1024; resultText.y = 800; game.addChild(resultText); // Play again button playAgainButton = new MenuButton('Play Again', 'playAgain'); playAgainButton.x = 1024; playAgainButton.y = 1000; playAgainButton.down = function () { initializeMenu(); }; game.addChild(playAgainButton); } // Initialize menu on start initializeMenu(); game.update = function () { // Main game loop - most logic handled in event handlers };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var GridCell = Container.expand(function (row, col) {
var self = Container.call(this);
var cellBg = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
self.row = row;
self.col = col;
self.symbol = null;
self.symbolGraphic = null;
self.down = function (x, y, obj) {
if (gameState === 'playing' && self.symbol === null && !waitingForAI) {
self.placeSymbol(playerSymbol);
LK.getSound('placeSymbol').play();
if (checkWin(gameBoard, playerSymbol)) {
gameState = 'playerWin';
showGameResult('You Win!');
LK.getSound('gameWin').play();
return;
}
if (checkDraw(gameBoard)) {
gameState = 'draw';
showGameResult('Draw!');
LK.getSound('gameDraw').play();
return;
}
waitingForAI = true;
LK.setTimeout(function () {
makeAIMove();
waitingForAI = false;
}, 500);
}
};
self.placeSymbol = function (symbol) {
if (self.symbol !== null) {
return;
}
self.symbol = symbol;
gameBoard[self.row][self.col] = symbol;
var assetName = symbol === 'X' ? 'xSymbol' : 'oSymbol';
self.symbolGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
tween(self.symbolGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.bounceOut
});
};
return self;
});
var MenuButton = Container.expand(function (text, difficulty) {
var self = Container.call(this);
var buttonBg = self.attachAsset('buttonBackground', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.difficulty = difficulty;
self.isHovered = false;
self.down = function (x, y, obj) {
tween(buttonBg, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
currentDifficulty = self.difficulty;
gameState = 'playing';
initializeGame();
};
self.up = function (x, y, obj) {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
return self;
});
var TutorialOverlay = Container.expand(function (message) {
var self = Container.call(this);
var overlay = self.attachAsset('tutorialOverlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
var messageText = new Text2(message, {
size: 80,
fill: 0xFFFFFF
});
messageText.anchor.set(0.5, 0.5);
self.addChild(messageText);
self.down = function (x, y, obj) {
self.destroy();
tutorialStep++;
showNextTutorial();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var gameState = 'menu'; // menu, playing, playerWin, aiWin, draw
var currentDifficulty = 'EASY';
var gameBoard = [];
var gridCells = [];
var playerSymbol = 'X';
var aiSymbol = 'O';
var waitingForAI = false;
var tutorialStep = 0;
var tutorialOverlay = null;
// Menu elements
var menuButtons = [];
var gameGrid = null;
var resultText = null;
var playAgainButton = null;
// Tutorial messages
var tutorialMessages = ["Welcome to Tic-Tac-Toe!\nTap to continue...", "Goal: Get 3 symbols in a row\n(horizontal, vertical, or diagonal)", "You are X, AI is O\nYou go first - tap any cell!", "Watch how the AI responds\nto your moves!", "Good luck! Tap to start playing..."];
function initializeMenu() {
gameState = 'menu';
// Clear existing elements
if (gameGrid) {
gameGrid.destroy();
gameGrid = null;
}
if (resultText) {
resultText.destroy();
resultText = null;
}
if (playAgainButton) {
playAgainButton.destroy();
playAgainButton = null;
}
// Create menu buttons
var difficulties = ['BEGINNER', 'EASY', 'MEDIUM', 'HARD'];
var buttonSpacing = 200;
var startY = 1366 - difficulties.length * buttonSpacing / 2;
for (var i = 0; i < difficulties.length; i++) {
var button = new MenuButton(difficulties[i], difficulties[i]);
button.x = 1024;
button.y = startY + i * buttonSpacing;
menuButtons.push(button);
game.addChild(button);
}
// Title
var titleText = new Text2('Tic-Tac-Toe Master', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
}
function initializeGame() {
// Clear menu
for (var i = 0; i < menuButtons.length; i++) {
menuButtons[i].destroy();
}
menuButtons = [];
// Random symbol assignment
if (Math.random() < 0.5) {
playerSymbol = 'X';
aiSymbol = 'O';
} else {
playerSymbol = 'O';
aiSymbol = 'X';
}
// Initialize game board
gameBoard = [[null, null, null], [null, null, null], [null, null, null]];
// Create grid
createGrid();
// Show tutorial for beginner mode
if (currentDifficulty === 'BEGINNER') {
tutorialStep = 0;
showNextTutorial();
}
}
function createGrid() {
gameGrid = new Container();
game.addChild(gameGrid);
// Center the grid
gameGrid.x = 1024;
gameGrid.y = 1366;
// Create grid background
var gridBg = gameGrid.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Create cells
gridCells = [];
for (var row = 0; row < 3; row++) {
gridCells[row] = [];
for (var col = 0; col < 3; col++) {
var cell = new GridCell(row, col);
cell.x = (col - 1) * 200;
cell.y = (row - 1) * 200;
gridCells[row][col] = cell;
gameGrid.addChild(cell);
}
}
}
function showNextTutorial() {
if (currentDifficulty !== 'BEGINNER' || tutorialStep >= tutorialMessages.length) {
return;
}
tutorialOverlay = new TutorialOverlay(tutorialMessages[tutorialStep]);
tutorialOverlay.x = 1024;
tutorialOverlay.y = 1366;
game.addChild(tutorialOverlay);
}
function makeAIMove() {
var availableMoves = [];
// Find available moves
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (gameBoard[row][col] === null) {
availableMoves.push({
row: row,
col: col
});
}
}
}
if (availableMoves.length === 0) {
return;
}
var move = null;
switch (currentDifficulty) {
case 'BEGINNER':
case 'EASY':
// Random move
move = availableMoves[Math.floor(Math.random() * availableMoves.length)];
break;
case 'MEDIUM':
// Try to win, then block, then random
move = findWinningMove(gameBoard, aiSymbol) || findWinningMove(gameBoard, playerSymbol) || availableMoves[Math.floor(Math.random() * availableMoves.length)];
break;
case 'HARD':
// Strategic AI
move = getBestMove(gameBoard, aiSymbol);
break;
}
if (move) {
gridCells[move.row][move.col].placeSymbol(aiSymbol);
if (checkWin(gameBoard, aiSymbol)) {
gameState = 'aiWin';
showGameResult('AI Wins!');
LK.getSound('gameLose').play();
} else if (checkDraw(gameBoard)) {
gameState = 'draw';
showGameResult('Draw!');
LK.getSound('gameDraw').play();
}
}
}
function findWinningMove(board, symbol) {
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === null) {
board[row][col] = symbol;
if (checkWin(board, symbol)) {
board[row][col] = null;
return {
row: row,
col: col
};
}
board[row][col] = null;
}
}
}
return null;
}
function getBestMove(board, symbol) {
var bestScore = -Infinity;
var bestMove = null;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === null) {
board[row][col] = symbol;
var score = minimax(board, 0, false, symbol);
board[row][col] = null;
if (score > bestScore) {
bestScore = score;
bestMove = {
row: row,
col: col
};
}
}
}
}
return bestMove;
}
function minimax(board, depth, isMaximizing, aiSym) {
var playerSym = aiSym === 'X' ? 'O' : 'X';
if (checkWin(board, aiSym)) {
return 10 - depth;
}
if (checkWin(board, playerSym)) {
return depth - 10;
}
if (checkDraw(board)) {
return 0;
}
if (isMaximizing) {
var bestScore = -Infinity;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === null) {
board[row][col] = aiSym;
var score = minimax(board, depth + 1, false, aiSym);
board[row][col] = null;
bestScore = Math.max(score, bestScore);
}
}
}
return bestScore;
} else {
var bestScore = Infinity;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === null) {
board[row][col] = playerSym;
var score = minimax(board, depth + 1, true, aiSym);
board[row][col] = null;
bestScore = Math.min(score, bestScore);
}
}
}
return bestScore;
}
}
function checkWin(board, symbol) {
// Check rows
for (var row = 0; row < 3; row++) {
if (board[row][0] === symbol && board[row][1] === symbol && board[row][2] === symbol) {
return true;
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (board[0][col] === symbol && board[1][col] === symbol && board[2][col] === symbol) {
return true;
}
}
// Check diagonals
if (board[0][0] === symbol && board[1][1] === symbol && board[2][2] === symbol) {
return true;
}
if (board[0][2] === symbol && board[1][1] === symbol && board[2][0] === symbol) {
return true;
}
return false;
}
function checkDraw(board) {
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
if (board[row][col] === null) {
return false;
}
}
}
return true;
}
function showGameResult(message) {
resultText = new Text2(message, {
size: 100,
fill: 0xFFFFFF
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 800;
game.addChild(resultText);
// Play again button
playAgainButton = new MenuButton('Play Again', 'playAgain');
playAgainButton.x = 1024;
playAgainButton.y = 1000;
playAgainButton.down = function () {
initializeMenu();
};
game.addChild(playAgainButton);
}
// Initialize menu on start
initializeMenu();
game.update = function () {
// Main game loop - most logic handled in event handlers
};