/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Cell = Container.expand(function () { var self = Container.call(this); var cellBg = self.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.row = 0; self.col = 0; self.value = 0; // 0 = empty, 1 = X, 2 = O self.marker = null; self.highlight = function () { tween(cellBg, { alpha: 1 }, { duration: 200 }); }; self.unhighlight = function () { tween(cellBg, { alpha: 0.8 }, { duration: 200 }); }; self.setMarker = function (playerValue) { if (self.value !== 0) { return false; } self.value = playerValue; if (playerValue === 1) { self.marker = self.attachAsset('x_marker', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); } else { self.marker = self.attachAsset('o_marker', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); } tween(self.marker, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); return true; }; self.reset = function () { self.value = 0; if (self.marker) { tween(self.marker, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (self.marker) { self.marker.destroy(); self.marker = null; } } }); } }; self.down = function (x, y, obj) { if (gameState === STATE_PLAYER_TURN && self.value === 0 && (isTwoPlayerMode || currentPlayer === 1)) { if (self.setMarker(currentPlayer)) { LK.getSound('place').play(); checkGameState(); if (gameState === STATE_PLAYER_TURN) { // Game continues, switch player currentPlayer = currentPlayer === 1 ? 2 : 1; updateTurnIndicator(); } } } }; return self; }); var WinLine = Container.expand(function () { var self = Container.call(this); var line = self.attachAsset('board_bg', { anchorX: 0.5, anchorY: 0.5, alpha: 0, tint: 0x00ff00 }); line.width = 800; line.height = 20; self.showLine = function (startX, startY, endX, endY) { self.x = (startX + endX) / 2; self.y = (startY + endY) / 2; // Calculate rotation and length var dx = endX - startX; var dy = endY - startY; var distance = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); line.width = distance; line.rotation = angle; tween(line, { alpha: 0.7 }, { duration: 500 }); }; self.hide = function () { tween(line, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x004488 }); /**** * Game Code ****/ // Game constants var AIPlayer = function AIPlayer() { this.makeMove = function () { if (gameState !== STATE_PLAYER_TURN) { return; } // Simple AI: choose the first available cell for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { if (gameBoard[row][col].value === 0) { gameBoard[row][col].setMarker(currentPlayer); LK.getSound('place').play(); checkGameState(); if (gameState === STATE_PLAYER_TURN) { currentPlayer = currentPlayer === 1 ? 2 : 1; updateTurnIndicator(); } return; } } } }; }; var GRID_SIZE = 4; var CELL_SIZE = 180; var CELL_SPACING = 10; var BOARD_SIZE = CELL_SIZE * GRID_SIZE + CELL_SPACING * (GRID_SIZE - 1); // Game states var STATE_PLAYER_TURN = 0; var STATE_GAME_OVER = 1; // AI player var aiPlayer = new AIPlayer(); var gameBoard = []; var gameState = STATE_PLAYER_TURN; var currentPlayer = 1; // 1 = X, 2 = O var isTwoPlayerMode = true; // Toggle for two player mode var winningCombos = []; // GUI elements var turnIndicator; var gameOverText; // Create board background var boardBg = LK.getAsset('board_bg', { anchorX: 0.5, anchorY: 0.5, width: BOARD_SIZE + 40, height: BOARD_SIZE + 40 }); boardBg.x = 2048 / 2; boardBg.y = 2732 / 2; game.addChild(boardBg); // Initialize turn indicator turnIndicator = new Text2('X\'s Turn', { size: 80, fill: 0xFFFFFF }); turnIndicator.anchor.set(0.5, 0); LK.gui.top.addChild(turnIndicator); turnIndicator.y = 100; // Initialize game over text (hidden initially) gameOverText = new Text2('', { size: 100, fill: 0xFFFFFF }); gameOverText.anchor.set(0.5, 0.5); gameOverText.visible = false; LK.gui.center.addChild(gameOverText); function initializeBoard() { // Clear any existing board if (gameBoard.length > 0) { for (var i = 0; i < gameBoard.length; i++) { for (var j = 0; j < gameBoard[i].length; j++) { if (gameBoard[i][j]) { gameBoard[i][j].destroy(); } } } } // Create new board gameBoard = []; var startX = 2048 / 2 - BOARD_SIZE / 2 + CELL_SIZE / 2; var startY = 2732 / 2 - BOARD_SIZE / 2 + CELL_SIZE / 2; for (var row = 0; row < GRID_SIZE; row++) { gameBoard[row] = []; for (var col = 0; col < GRID_SIZE; col++) { var cell = new Cell(); cell.row = row; cell.col = col; cell.x = startX + col * (CELL_SIZE + CELL_SPACING); cell.y = startY + row * (CELL_SIZE + CELL_SPACING); gameBoard[row][col] = cell; game.addChild(cell); } } // Define winning combinations defineWinningCombinations(); } function defineWinningCombinations() { winningCombos = []; // Rows for (var row = 0; row < GRID_SIZE; row++) { winningCombos.push([{ row: row, col: 0 }, { row: row, col: 1 }, { row: row, col: 2 }, { row: row, col: 3 }]); } // Columns for (var col = 0; col < GRID_SIZE; col++) { winningCombos.push([{ row: 0, col: col }, { row: 1, col: col }, { row: 2, col: col }, { row: 3, col: col }]); } // Diagonals winningCombos.push([{ row: 0, col: 0 }, { row: 1, col: 1 }, { row: 2, col: 2 }, { row: 3, col: 3 }]); winningCombos.push([{ row: 0, col: 3 }, { row: 1, col: 2 }, { row: 2, col: 1 }, { row: 3, col: 0 }]); } function checkGameState() { // Check for win var winResult = checkForWin(); if (winResult) { gameState = STATE_GAME_OVER; showWinLine(winResult.combo); updateGameOverText(currentPlayer === 1 ? "X Wins!" : "O Wins!"); LK.getSound('win').play(); LK.setScore(LK.getScore() + 1); return; } // Check for draw if (checkForDraw()) { gameState = STATE_GAME_OVER; updateGameOverText("Draw!"); LK.getSound('draw').play(); return; } } function checkForWin() { for (var i = 0; i < winningCombos.length; i++) { var combo = winningCombos[i]; var first = gameBoard[combo[0].row][combo[0].col].value; if (first !== 0) { var isWin = true; for (var j = 1; j < combo.length; j++) { if (gameBoard[combo[j].row][combo[j].col].value !== first) { isWin = false; break; } } if (isWin) { return { player: first, combo: combo }; } } } return null; } function checkForDraw() { for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { if (gameBoard[row][col].value === 0) { return false; // Still empty cells } } } return true; } function showWinLine(combo) { var startCell = gameBoard[combo[0].row][combo[0].col]; var endCell = gameBoard[combo[combo.length - 1].row][combo[combo.length - 1].col]; var winLine = new WinLine(); game.addChild(winLine); winLine.showLine(startCell.x, startCell.y, endCell.x, endCell.y); } function updateTurnIndicator() { turnIndicator.setText(currentPlayer === 1 ? "X's Turn" : "O's Turn"); } function updateGameOverText(text) { gameOverText.setText(text); gameOverText.visible = true; // Show restart button after a delay LK.setTimeout(function () { LK.showGameOver(); }, 2000); } function restartGame() { // Reset game state gameState = STATE_PLAYER_TURN; currentPlayer = 1; // Reset board for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { gameBoard[row][col].reset(); } } // Update UI updateTurnIndicator(); gameOverText.visible = false; } // Game setup initializeBoard(); updateTurnIndicator(); // Play background music LK.playMusic('bgmusic', { loop: true, fade: { start: 0, end: 0.3, duration: 1000 } }); // Game main loop game.update = function () { // This is called every frame if (!isTwoPlayerMode && currentPlayer === 2 && gameState === STATE_PLAYER_TURN) { aiPlayer.makeMove(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cell = Container.expand(function () {
var self = Container.call(this);
var cellBg = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.row = 0;
self.col = 0;
self.value = 0; // 0 = empty, 1 = X, 2 = O
self.marker = null;
self.highlight = function () {
tween(cellBg, {
alpha: 1
}, {
duration: 200
});
};
self.unhighlight = function () {
tween(cellBg, {
alpha: 0.8
}, {
duration: 200
});
};
self.setMarker = function (playerValue) {
if (self.value !== 0) {
return false;
}
self.value = playerValue;
if (playerValue === 1) {
self.marker = self.attachAsset('x_marker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
} else {
self.marker = self.attachAsset('o_marker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
}
tween(self.marker, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
return true;
};
self.reset = function () {
self.value = 0;
if (self.marker) {
tween(self.marker, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (self.marker) {
self.marker.destroy();
self.marker = null;
}
}
});
}
};
self.down = function (x, y, obj) {
if (gameState === STATE_PLAYER_TURN && self.value === 0 && (isTwoPlayerMode || currentPlayer === 1)) {
if (self.setMarker(currentPlayer)) {
LK.getSound('place').play();
checkGameState();
if (gameState === STATE_PLAYER_TURN) {
// Game continues, switch player
currentPlayer = currentPlayer === 1 ? 2 : 1;
updateTurnIndicator();
}
}
}
};
return self;
});
var WinLine = Container.expand(function () {
var self = Container.call(this);
var line = self.attachAsset('board_bg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
tint: 0x00ff00
});
line.width = 800;
line.height = 20;
self.showLine = function (startX, startY, endX, endY) {
self.x = (startX + endX) / 2;
self.y = (startY + endY) / 2;
// Calculate rotation and length
var dx = endX - startX;
var dy = endY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
line.width = distance;
line.rotation = angle;
tween(line, {
alpha: 0.7
}, {
duration: 500
});
};
self.hide = function () {
tween(line, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x004488
});
/****
* Game Code
****/
// Game constants
var AIPlayer = function AIPlayer() {
this.makeMove = function () {
if (gameState !== STATE_PLAYER_TURN) {
return;
}
// Simple AI: choose the first available cell
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (gameBoard[row][col].value === 0) {
gameBoard[row][col].setMarker(currentPlayer);
LK.getSound('place').play();
checkGameState();
if (gameState === STATE_PLAYER_TURN) {
currentPlayer = currentPlayer === 1 ? 2 : 1;
updateTurnIndicator();
}
return;
}
}
}
};
};
var GRID_SIZE = 4;
var CELL_SIZE = 180;
var CELL_SPACING = 10;
var BOARD_SIZE = CELL_SIZE * GRID_SIZE + CELL_SPACING * (GRID_SIZE - 1);
// Game states
var STATE_PLAYER_TURN = 0;
var STATE_GAME_OVER = 1;
// AI player
var aiPlayer = new AIPlayer();
var gameBoard = [];
var gameState = STATE_PLAYER_TURN;
var currentPlayer = 1; // 1 = X, 2 = O
var isTwoPlayerMode = true; // Toggle for two player mode
var winningCombos = [];
// GUI elements
var turnIndicator;
var gameOverText;
// Create board background
var boardBg = LK.getAsset('board_bg', {
anchorX: 0.5,
anchorY: 0.5,
width: BOARD_SIZE + 40,
height: BOARD_SIZE + 40
});
boardBg.x = 2048 / 2;
boardBg.y = 2732 / 2;
game.addChild(boardBg);
// Initialize turn indicator
turnIndicator = new Text2('X\'s Turn', {
size: 80,
fill: 0xFFFFFF
});
turnIndicator.anchor.set(0.5, 0);
LK.gui.top.addChild(turnIndicator);
turnIndicator.y = 100;
// Initialize game over text (hidden initially)
gameOverText = new Text2('', {
size: 100,
fill: 0xFFFFFF
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.visible = false;
LK.gui.center.addChild(gameOverText);
function initializeBoard() {
// Clear any existing board
if (gameBoard.length > 0) {
for (var i = 0; i < gameBoard.length; i++) {
for (var j = 0; j < gameBoard[i].length; j++) {
if (gameBoard[i][j]) {
gameBoard[i][j].destroy();
}
}
}
}
// Create new board
gameBoard = [];
var startX = 2048 / 2 - BOARD_SIZE / 2 + CELL_SIZE / 2;
var startY = 2732 / 2 - BOARD_SIZE / 2 + CELL_SIZE / 2;
for (var row = 0; row < GRID_SIZE; row++) {
gameBoard[row] = [];
for (var col = 0; col < GRID_SIZE; col++) {
var cell = new Cell();
cell.row = row;
cell.col = col;
cell.x = startX + col * (CELL_SIZE + CELL_SPACING);
cell.y = startY + row * (CELL_SIZE + CELL_SPACING);
gameBoard[row][col] = cell;
game.addChild(cell);
}
}
// Define winning combinations
defineWinningCombinations();
}
function defineWinningCombinations() {
winningCombos = [];
// Rows
for (var row = 0; row < GRID_SIZE; row++) {
winningCombos.push([{
row: row,
col: 0
}, {
row: row,
col: 1
}, {
row: row,
col: 2
}, {
row: row,
col: 3
}]);
}
// Columns
for (var col = 0; col < GRID_SIZE; col++) {
winningCombos.push([{
row: 0,
col: col
}, {
row: 1,
col: col
}, {
row: 2,
col: col
}, {
row: 3,
col: col
}]);
}
// Diagonals
winningCombos.push([{
row: 0,
col: 0
}, {
row: 1,
col: 1
}, {
row: 2,
col: 2
}, {
row: 3,
col: 3
}]);
winningCombos.push([{
row: 0,
col: 3
}, {
row: 1,
col: 2
}, {
row: 2,
col: 1
}, {
row: 3,
col: 0
}]);
}
function checkGameState() {
// Check for win
var winResult = checkForWin();
if (winResult) {
gameState = STATE_GAME_OVER;
showWinLine(winResult.combo);
updateGameOverText(currentPlayer === 1 ? "X Wins!" : "O Wins!");
LK.getSound('win').play();
LK.setScore(LK.getScore() + 1);
return;
}
// Check for draw
if (checkForDraw()) {
gameState = STATE_GAME_OVER;
updateGameOverText("Draw!");
LK.getSound('draw').play();
return;
}
}
function checkForWin() {
for (var i = 0; i < winningCombos.length; i++) {
var combo = winningCombos[i];
var first = gameBoard[combo[0].row][combo[0].col].value;
if (first !== 0) {
var isWin = true;
for (var j = 1; j < combo.length; j++) {
if (gameBoard[combo[j].row][combo[j].col].value !== first) {
isWin = false;
break;
}
}
if (isWin) {
return {
player: first,
combo: combo
};
}
}
}
return null;
}
function checkForDraw() {
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (gameBoard[row][col].value === 0) {
return false; // Still empty cells
}
}
}
return true;
}
function showWinLine(combo) {
var startCell = gameBoard[combo[0].row][combo[0].col];
var endCell = gameBoard[combo[combo.length - 1].row][combo[combo.length - 1].col];
var winLine = new WinLine();
game.addChild(winLine);
winLine.showLine(startCell.x, startCell.y, endCell.x, endCell.y);
}
function updateTurnIndicator() {
turnIndicator.setText(currentPlayer === 1 ? "X's Turn" : "O's Turn");
}
function updateGameOverText(text) {
gameOverText.setText(text);
gameOverText.visible = true;
// Show restart button after a delay
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
function restartGame() {
// Reset game state
gameState = STATE_PLAYER_TURN;
currentPlayer = 1;
// Reset board
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
gameBoard[row][col].reset();
}
}
// Update UI
updateTurnIndicator();
gameOverText.visible = false;
}
// Game setup
initializeBoard();
updateTurnIndicator();
// Play background music
LK.playMusic('bgmusic', {
loop: true,
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Game main loop
game.update = function () {
// This is called every frame
if (!isTwoPlayerMode && currentPlayer === 2 && gameState === STATE_PLAYER_TURN) {
aiPlayer.makeMove();
}
};