/**** * 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 cellGraphics = self.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); self.row = 0; self.col = 0; self.piece = null; self.down = function (x, y, obj) { if (!self.piece && currentPlayer === 0) { placePiece(self.row, self.col); } }; return self; }); var CoordButton = Container.expand(function () { var self = Container.call(this); var buttonBg = self.attachAsset('coordButton', { anchorX: 0.5, anchorY: 0.5 }); self.down = function (x, y, obj) { toggleHistory(); }; return self; }); var HistoryPanel = Container.expand(function () { var self = Container.call(this); var bg = self.attachAsset('historyBg', { anchorX: 0.5, anchorY: 0.5 }); var titleText = new Text2('Move History', { size: 65, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.y = -960; self.addChild(titleText); self.historyText = new Text2('', { size: 60, fill: 0xFFFFFF }); self.historyText.anchor.set(0.5, 0); self.historyText.y = -880; self.addChild(self.historyText); self.updateHistory = function () { var historyString = ''; for (var i = 0; i < moveHistory.length; i++) { var move = moveHistory[i]; var player = move.player === 0 ? 'Orange' : 'Grey'; historyString += player + ': ' + move.coord + '\n'; } self.historyText.setText(historyString); }; self.down = function (x, y, obj) { toggleHistory(); }; return self; }); var IntroButton = Container.expand(function () { var self = Container.call(this); var buttonBg = self.attachAsset('startButton', { anchorX: 0.5, anchorY: 0.5 }); self.down = function (x, y, obj) { if (introScreen && !levelSelectionScreen) { introScreen.destroy(); introScreen = null; createLevelSelection(); } }; return self; }); var LevelButton = Container.expand(function () { var self = Container.call(this); self.levelNumber = 1; self.isSelected = false; self.buttonBg = self.attachAsset('levelButton', { anchorX: 0.5, anchorY: 0.5 }); self.levelText = new Text2('1', { size: 60, fill: 0xFFFFFF }); self.levelText.anchor.set(0.5, 0.5); self.addChild(self.levelText); self.setLevel = function (level) { self.levelNumber = level; self.levelText.setText(level.toString()); }; self.setSelected = function (selected) { if (self.isSelected === selected) return; self.isSelected = selected; self.removeChild(self.buttonBg); if (selected) { self.buttonBg = self.attachAsset('selectedLevelButton', { anchorX: 0.5, anchorY: 0.5 }); } else { self.buttonBg = self.attachAsset('levelButton', { anchorX: 0.5, anchorY: 0.5 }); } // Make sure text is on top self.removeChild(self.levelText); self.addChild(self.levelText); }; self.down = function (x, y, obj) { if (!gameStarted && levelSelectionScreen) { selectLevel(self.levelNumber); } }; return self; }); var Piece = Container.expand(function () { var self = Container.call(this); self.player = 0; // 0 for orange, 1 for grey self.graphics = null; self.setPlayer = function (player) { if (self.graphics) { self.graphics.destroy(); } self.player = player; var assetId = player === 0 ? 'orangePiece' : 'greyPiece'; self.graphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); }; self.flip = function () { tween(self, { scaleX: 0 }, { duration: 150, onFinish: function onFinish() { self.setPlayer(1 - self.player); tween(self, { scaleX: 1 }, { duration: 150 }); } }); }; return self; }); var StartButton = Container.expand(function () { var self = Container.call(this); var buttonBg = self.attachAsset('startButton', { anchorX: 0.5, anchorY: 0.5 }); self.down = function (x, y, obj) { if (levelSelectionScreen && !gameStarted && selectedLevel > 0) { levelSelectionScreen.destroy(); levelSelectionScreen = null; gameStarted = true; initializeGame(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a3a1a }); /**** * Game Code ****/ var BOARD_SIZE = 10; var board = []; var cells = []; var currentPlayer = 0; // 0 for orange, 1 for grey var moveHistory = []; var historyPanel = null; var historyVisible = false; var introScreen = null; var levelSelectionScreen = null; var gameStarted = false; var boardContainer = null; var selectedLevel = 0; var levelButtons = []; var levelStartButton = null; var currentDifficulty = 1; // Stores the calculated difficulty for current level // Create intro screen introScreen = new Container(); game.addChild(introScreen); // Add intro background var introBg = LK.getAsset('introBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); introScreen.addChild(introBg); // Add intro button var introButton = new IntroButton(); introButton.x = 1024; introButton.y = 2732 - 200; // 200px from bottom introScreen.addChild(introButton); // Game variables that need to be accessible var orangeCountText = null; var greyCountText = null; var playerText = null; var coordButton = null; var letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']; var setupMode = false; function createLevelSelection() { // Create level selection screen levelSelectionScreen = new Container(); game.addChild(levelSelectionScreen); // Add level selection background var levelBg = LK.getAsset('levelsBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); levelSelectionScreen.addChild(levelBg); // Add title text var titleText = new Text2('SELECT LEVEL', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; levelSelectionScreen.addChild(titleText); // Create 50 level buttons in a 5x10 grid to fill the screen var buttonSpacing = 180; var startX = 1024 - (5 * buttonSpacing - buttonSpacing) / 2; var startY = 650; levelButtons = []; // Reset level buttons array for (var i = 0; i < 50; i++) { var levelButton = new LevelButton(); levelButton.setLevel(i + 1); var row = Math.floor(i / 5); var col = i % 5; levelButton.x = startX + col * buttonSpacing; levelButton.y = startY + row * buttonSpacing; levelButtons.push(levelButton); levelSelectionScreen.addChild(levelButton); } // Add start button (initially hidden) levelStartButton = new StartButton(); levelStartButton.x = 1024; levelStartButton.y = 2732 - 200; // 200px from bottom levelStartButton.visible = false; levelSelectionScreen.addChild(levelStartButton); } function getDifficultySettings(level) { // Progressive difficulty scaling from level 1-50 var settings = { aiType: 'easy', // easy, medium, hard, expert startingPieces: 4, // Number of starting pieces (2x2, 3x3, etc.) aiDelay: 500, // AI thinking time in ms cornerBonus: 0, // Strategic corner bonus for AI edgeBonus: 0, // Strategic edge bonus for AI mobilityWeight: 0 // How much AI considers limiting opponent moves }; if (level <= 10) { // Levels 1-10: Easy AI, standard setup settings.aiType = 'easy'; settings.aiDelay = 500; } else if (level <= 20) { // Levels 11-20: Medium AI with slight strategic thinking settings.aiType = 'medium'; settings.aiDelay = 400; settings.cornerBonus = 10; } else if (level <= 35) { // Levels 21-35: Hard AI with better strategy settings.aiType = 'hard'; settings.aiDelay = 300; settings.cornerBonus = 20; settings.edgeBonus = 5; settings.mobilityWeight = 5; } else { // Levels 36-50: Expert AI with advanced strategy settings.aiType = 'expert'; settings.aiDelay = 200; settings.cornerBonus = 30; settings.edgeBonus = 10; settings.mobilityWeight = 10; } // Adjust starting pieces based on level if (level <= 15) { settings.startingPieces = 4; // 2x2 center } else if (level <= 30) { settings.startingPieces = 6; // 2x3 center } else { settings.startingPieces = 8; // 2x4 center - more challenging start } return settings; } function selectLevel(level) { selectedLevel = level; currentDifficulty = getDifficultySettings(level); // Update all buttons - deselect all first for (var i = 0; i < levelButtons.length; i++) { levelButtons[i].setSelected(false); } // Select the chosen level button if (level >= 1 && level <= 50) { levelButtons[level - 1].setSelected(true); } // Show start button if (levelStartButton) { levelStartButton.visible = true; } } function initializeGame() { // Create board container boardContainer = new Container(); boardContainer.x = 1024; boardContainer.y = 1366; game.addChild(boardContainer); // Create board background var boardBg = LK.getAsset('boardBg', { anchorX: 0.5, anchorY: 0.5 }); boardContainer.addChild(boardBg); // Create piece count texts orangeCountText = new Text2('Orange: 2', { size: 80, fill: 0xFF8C00 }); orangeCountText.anchor.set(0, 0.5); orangeCountText.x = -900; orangeCountText.y = 1000; boardContainer.addChild(orangeCountText); greyCountText = new Text2('Grey: 2', { size: 80, fill: 0x808080 }); greyCountText.anchor.set(1, 0.5); greyCountText.x = 900; greyCountText.y = 1000; boardContainer.addChild(greyCountText); // Initialize board array and create cells var cellSpacing = 1850 / BOARD_SIZE; for (var row = 0; row < BOARD_SIZE; row++) { board[row] = []; cells[row] = []; for (var col = 0; col < BOARD_SIZE; col++) { board[row][col] = null; var cell = new Cell(); cell.row = row; cell.col = col; cell.x = (col - 4.5) * cellSpacing; cell.y = (row - 4.5) * cellSpacing; boardContainer.addChild(cell); cells[row][col] = cell; } } // Create coordinate labels var cellSpacing = 1850 / BOARD_SIZE; for (var i = 0; i < BOARD_SIZE; i++) { // Column labels (A-J) var colLabel = new Text2(letters[i], { size: 40, fill: 0xFFFFFF }); colLabel.anchor.set(0.5, 0.5); colLabel.x = (i - 4.5) * cellSpacing; colLabel.y = -945; boardContainer.addChild(colLabel); // Row labels (1-10) var rowLabel = new Text2((i + 1).toString(), { size: 40, fill: 0xFFFFFF }); rowLabel.anchor.set(0.5, 0.5); rowLabel.x = -945; rowLabel.y = (i - 4.5) * cellSpacing; boardContainer.addChild(rowLabel); } // Create current player indicator playerText = new Text2('Current Player: Orange', { size: 60, fill: 0xFF8C00 }); playerText.anchor.set(0.5, 0); playerText.y = 100; LK.gui.top.addChild(playerText); // Create coordinates button coordButton = new CoordButton(); coordButton.x = -100; coordButton.y = 100; LK.gui.topRight.addChild(coordButton); // Create history panel (initially hidden) historyPanel = new HistoryPanel(); historyPanel.visible = false; game.addChild(historyPanel); historyPanel.x = 1024; historyPanel.y = 1366; // Initialize with starting pieces based on difficulty setupMode = true; var difficulty = getDifficultySettings(selectedLevel); if (difficulty.startingPieces === 4) { // Standard 2x2 center placePiece(4, 4); placePiece(4, 5); placePiece(5, 5); placePiece(5, 4); } else if (difficulty.startingPieces === 6) { // 2x3 center setup - more challenging placePiece(4, 4); placePiece(4, 5); placePiece(5, 4); placePiece(5, 5); placePiece(4, 6); placePiece(5, 6); } else if (difficulty.startingPieces === 8) { // 2x4 center setup - most challenging placePiece(4, 3); placePiece(4, 4); placePiece(4, 5); placePiece(4, 6); placePiece(5, 3); placePiece(5, 4); placePiece(5, 5); placePiece(5, 6); } setupMode = false; // Reset to orange player's turn currentPlayer = 0; updatePlayerText(); updatePieceCounts(); } // Game functions function getCoordinate(row, col) { return letters[col] + (row + 1); } function placePiece(row, col) { if (!gameStarted || board[row][col] !== null) return; // Create and place piece var piece = new Piece(); piece.setPlayer(currentPlayer); cells[row][col].addChild(piece); cells[row][col].piece = piece; board[row][col] = currentPlayer; // Play place sound LK.getSound('place').play(); // Add to move history moveHistory.push({ player: currentPlayer, coord: getCoordinate(row, col) }); // Update history panel if visible if (historyVisible && historyPanel) { historyPanel.updateHistory(); } // Check for flips in all directions var directions = [[-1, 0], [1, 0], [0, -1], [0, 1], // vertical and horizontal [-1, -1], [-1, 1], [1, -1], [1, 1] // diagonals ]; var totalFlips = 0; for (var d = 0; d < directions.length; d++) { var flips = checkDirection(row, col, directions[d][0], directions[d][1]); if (flips.length > 0) { flipPieces(flips); totalFlips += flips.length; } } if (totalFlips > 0) { LK.getSound('flip').play(); } // Update piece counts updatePieceCounts(); // Switch player currentPlayer = 1 - currentPlayer; updatePlayerText(); // Check for game over checkGameOver(); // If it's now grey's turn (AI), make AI move if (currentPlayer === 1 && !setupMode) { makeAIMove(); } } function checkDirection(row, col, dRow, dCol) { var flips = []; var r = row + dRow; var c = col + dCol; // Find opponent pieces while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE) { if (board[r][c] === null) { return []; } if (board[r][c] === currentPlayer) { return flips; } flips.push([r, c]); r += dRow; c += dCol; } return []; } function flipPieces(positions) { for (var i = 0; i < positions.length; i++) { var row = positions[i][0]; var col = positions[i][1]; board[row][col] = currentPlayer; cells[row][col].piece.flip(); } } function updatePlayerText() { var player = currentPlayer === 0 ? 'Orange' : 'Grey'; var color = currentPlayer === 0 ? 0xff8c00 : 0x808080; playerText.setText('Current Player: ' + player); // Remove old text and create new one with updated color LK.gui.top.removeChild(playerText); playerText = new Text2('Current Player: ' + player, { size: 60, fill: color }); playerText.anchor.set(0.5, 0); playerText.y = 100; LK.gui.top.addChild(playerText); } function updatePieceCounts() { var orangeCount = 0; var greyCount = 0; for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { if (board[row][col] === 0) { orangeCount++; } else if (board[row][col] === 1) { greyCount++; } } } orangeCountText.setText('Orange: ' + orangeCount); greyCountText.setText('Grey: ' + greyCount); } function checkGameOver() { // Don't check for game over during initial setup if (setupMode) return; var hasEmpty = false; var orangeCount = 0; var greyCount = 0; var hasValidMove = false; for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { if (board[row][col] === null) { hasEmpty = true; // Check if current player has valid move at this position if (!hasValidMove && isValidMove(row, col, currentPlayer)) { hasValidMove = true; } } else if (board[row][col] === 0) { orangeCount++; } else { greyCount++; } } } // Check for game over conditions if (!hasEmpty || !hasValidMove) { if (orangeCount > greyCount) { LK.showYouWin(); } else { LK.showGameOver(); } } } function toggleHistory() { historyVisible = !historyVisible; if (historyPanel) { historyPanel.visible = historyVisible; if (historyVisible) { historyPanel.updateHistory(); } } } // AI functions function isValidMove(row, col, player) { if (board[row][col] !== null) return false; var directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]]; for (var d = 0; d < directions.length; d++) { var flips = checkDirectionForPlayer(row, col, directions[d][0], directions[d][1], player); if (flips.length > 0) { return true; } } return false; } function checkDirectionForPlayer(row, col, dRow, dCol, player) { var flips = []; var r = row + dRow; var c = col + dCol; while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE) { if (board[r][c] === null) { return []; } if (board[r][c] === player) { return flips; } flips.push([r, c]); r += dRow; c += dCol; } return []; } function evaluateMove(row, col, player) { var directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]]; var totalFlips = 0; for (var d = 0; d < directions.length; d++) { var flips = checkDirectionForPlayer(row, col, directions[d][0], directions[d][1], player); totalFlips += flips.length; } var difficulty = getDifficultySettings(selectedLevel); var score = totalFlips; // Apply strategic bonuses based on difficulty level if (difficulty.aiType !== 'easy') { // Corner bonus - corners are very valuable if ((row === 0 || row === BOARD_SIZE - 1) && (col === 0 || col === BOARD_SIZE - 1)) { score += difficulty.cornerBonus; } // Edge bonus - edges are somewhat valuable else if (row === 0 || row === BOARD_SIZE - 1 || col === 0 || col === BOARD_SIZE - 1) { score += difficulty.edgeBonus; } // Mobility consideration - how many moves will opponent have after this move if (difficulty.mobilityWeight > 0) { var opponentMoves = countValidMovesAfterMove(row, col, player); score -= opponentMoves * difficulty.mobilityWeight; } // Avoid positions next to corners (they give opponent corner access) if (difficulty.aiType === 'hard' || difficulty.aiType === 'expert') { if (isNextToCorner(row, col)) { score -= 15; // Penalty for moves next to corners } } } return score; } function countValidMovesAfterMove(row, col, player) { // Simulate placing the piece and count opponent's valid moves var originalValue = board[row][col]; board[row][col] = player; var opponentPlayer = 1 - player; var count = 0; for (var r = 0; r < BOARD_SIZE; r++) { for (var c = 0; c < BOARD_SIZE; c++) { if (isValidMove(r, c, opponentPlayer)) { count++; } } } board[row][col] = originalValue; // Restore original state return count; } function isNextToCorner(row, col) { // Check if position is adjacent to a corner (bad strategic move) var corners = [[0, 0], [0, BOARD_SIZE - 1], [BOARD_SIZE - 1, 0], [BOARD_SIZE - 1, BOARD_SIZE - 1]]; for (var i = 0; i < corners.length; i++) { var cornerRow = corners[i][0]; var cornerCol = corners[i][1]; var rowDiff = Math.abs(row - cornerRow); var colDiff = Math.abs(col - cornerCol); if (rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1 || rowDiff === 1 && colDiff === 1) { // Only penalize if the corner is empty (don't avoid if corner is already taken) if (board[cornerRow][cornerCol] === null) { return true; } } } return false; } function makeAIMove() { var validMoves = []; var difficulty = getDifficultySettings(selectedLevel); // Find all valid moves for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { if (isValidMove(row, col, 1)) { validMoves.push({ row: row, col: col, score: evaluateMove(row, col, 1) }); } } } if (validMoves.length > 0) { var chosenMove; if (difficulty.aiType === 'easy') { // Easy AI: pick randomly from moves that flip at least 1 piece var movesWithFlips = []; for (var i = 0; i < validMoves.length; i++) { if (validMoves[i].score > 0) { movesWithFlips.push(validMoves[i]); } } if (movesWithFlips.length === 0) { movesWithFlips = validMoves; } var randomIndex = Math.floor(Math.random() * movesWithFlips.length); chosenMove = movesWithFlips[randomIndex]; } else if (difficulty.aiType === 'medium') { // Medium AI: pick from top 50% of moves validMoves.sort(function (a, b) { return b.score - a.score; }); var topHalf = Math.ceil(validMoves.length / 2); var randomIndex = Math.floor(Math.random() * topHalf); chosenMove = validMoves[randomIndex]; } else if (difficulty.aiType === 'hard') { // Hard AI: pick from top 25% of moves validMoves.sort(function (a, b) { return b.score - a.score; }); var topQuarter = Math.max(1, Math.ceil(validMoves.length / 4)); var randomIndex = Math.floor(Math.random() * topQuarter); chosenMove = validMoves[randomIndex]; } else { // expert // Expert AI: always pick the best move (with small randomness for variety) validMoves.sort(function (a, b) { return b.score - a.score; }); var bestScore = validMoves[0].score; var bestMoves = []; for (var i = 0; i < validMoves.length; i++) { if (validMoves[i].score === bestScore) { bestMoves.push(validMoves[i]); } else { break; // Stop when we leave the best moves } } var randomIndex = Math.floor(Math.random() * bestMoves.length); chosenMove = bestMoves[randomIndex]; } // Add difficulty-based delay to make AI move visible LK.setTimeout(function () { placePiece(chosenMove.row, chosenMove.col); }, difficulty.aiDelay); } } ;
/****
* 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 cellGraphics = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5
});
self.row = 0;
self.col = 0;
self.piece = null;
self.down = function (x, y, obj) {
if (!self.piece && currentPlayer === 0) {
placePiece(self.row, self.col);
}
};
return self;
});
var CoordButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('coordButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function (x, y, obj) {
toggleHistory();
};
return self;
});
var HistoryPanel = Container.expand(function () {
var self = Container.call(this);
var bg = self.attachAsset('historyBg', {
anchorX: 0.5,
anchorY: 0.5
});
var titleText = new Text2('Move History', {
size: 65,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.y = -960;
self.addChild(titleText);
self.historyText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
self.historyText.anchor.set(0.5, 0);
self.historyText.y = -880;
self.addChild(self.historyText);
self.updateHistory = function () {
var historyString = '';
for (var i = 0; i < moveHistory.length; i++) {
var move = moveHistory[i];
var player = move.player === 0 ? 'Orange' : 'Grey';
historyString += player + ': ' + move.coord + '\n';
}
self.historyText.setText(historyString);
};
self.down = function (x, y, obj) {
toggleHistory();
};
return self;
});
var IntroButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function (x, y, obj) {
if (introScreen && !levelSelectionScreen) {
introScreen.destroy();
introScreen = null;
createLevelSelection();
}
};
return self;
});
var LevelButton = Container.expand(function () {
var self = Container.call(this);
self.levelNumber = 1;
self.isSelected = false;
self.buttonBg = self.attachAsset('levelButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.levelText = new Text2('1', {
size: 60,
fill: 0xFFFFFF
});
self.levelText.anchor.set(0.5, 0.5);
self.addChild(self.levelText);
self.setLevel = function (level) {
self.levelNumber = level;
self.levelText.setText(level.toString());
};
self.setSelected = function (selected) {
if (self.isSelected === selected) return;
self.isSelected = selected;
self.removeChild(self.buttonBg);
if (selected) {
self.buttonBg = self.attachAsset('selectedLevelButton', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.buttonBg = self.attachAsset('levelButton', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Make sure text is on top
self.removeChild(self.levelText);
self.addChild(self.levelText);
};
self.down = function (x, y, obj) {
if (!gameStarted && levelSelectionScreen) {
selectLevel(self.levelNumber);
}
};
return self;
});
var Piece = Container.expand(function () {
var self = Container.call(this);
self.player = 0; // 0 for orange, 1 for grey
self.graphics = null;
self.setPlayer = function (player) {
if (self.graphics) {
self.graphics.destroy();
}
self.player = player;
var assetId = player === 0 ? 'orangePiece' : 'greyPiece';
self.graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.flip = function () {
tween(self, {
scaleX: 0
}, {
duration: 150,
onFinish: function onFinish() {
self.setPlayer(1 - self.player);
tween(self, {
scaleX: 1
}, {
duration: 150
});
}
});
};
return self;
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function (x, y, obj) {
if (levelSelectionScreen && !gameStarted && selectedLevel > 0) {
levelSelectionScreen.destroy();
levelSelectionScreen = null;
gameStarted = true;
initializeGame();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a3a1a
});
/****
* Game Code
****/
var BOARD_SIZE = 10;
var board = [];
var cells = [];
var currentPlayer = 0; // 0 for orange, 1 for grey
var moveHistory = [];
var historyPanel = null;
var historyVisible = false;
var introScreen = null;
var levelSelectionScreen = null;
var gameStarted = false;
var boardContainer = null;
var selectedLevel = 0;
var levelButtons = [];
var levelStartButton = null;
var currentDifficulty = 1; // Stores the calculated difficulty for current level
// Create intro screen
introScreen = new Container();
game.addChild(introScreen);
// Add intro background
var introBg = LK.getAsset('introBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
introScreen.addChild(introBg);
// Add intro button
var introButton = new IntroButton();
introButton.x = 1024;
introButton.y = 2732 - 200; // 200px from bottom
introScreen.addChild(introButton);
// Game variables that need to be accessible
var orangeCountText = null;
var greyCountText = null;
var playerText = null;
var coordButton = null;
var letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
var setupMode = false;
function createLevelSelection() {
// Create level selection screen
levelSelectionScreen = new Container();
game.addChild(levelSelectionScreen);
// Add level selection background
var levelBg = LK.getAsset('levelsBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
levelSelectionScreen.addChild(levelBg);
// Add title text
var titleText = new Text2('SELECT LEVEL', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
levelSelectionScreen.addChild(titleText);
// Create 50 level buttons in a 5x10 grid to fill the screen
var buttonSpacing = 180;
var startX = 1024 - (5 * buttonSpacing - buttonSpacing) / 2;
var startY = 650;
levelButtons = []; // Reset level buttons array
for (var i = 0; i < 50; i++) {
var levelButton = new LevelButton();
levelButton.setLevel(i + 1);
var row = Math.floor(i / 5);
var col = i % 5;
levelButton.x = startX + col * buttonSpacing;
levelButton.y = startY + row * buttonSpacing;
levelButtons.push(levelButton);
levelSelectionScreen.addChild(levelButton);
}
// Add start button (initially hidden)
levelStartButton = new StartButton();
levelStartButton.x = 1024;
levelStartButton.y = 2732 - 200; // 200px from bottom
levelStartButton.visible = false;
levelSelectionScreen.addChild(levelStartButton);
}
function getDifficultySettings(level) {
// Progressive difficulty scaling from level 1-50
var settings = {
aiType: 'easy',
// easy, medium, hard, expert
startingPieces: 4,
// Number of starting pieces (2x2, 3x3, etc.)
aiDelay: 500,
// AI thinking time in ms
cornerBonus: 0,
// Strategic corner bonus for AI
edgeBonus: 0,
// Strategic edge bonus for AI
mobilityWeight: 0 // How much AI considers limiting opponent moves
};
if (level <= 10) {
// Levels 1-10: Easy AI, standard setup
settings.aiType = 'easy';
settings.aiDelay = 500;
} else if (level <= 20) {
// Levels 11-20: Medium AI with slight strategic thinking
settings.aiType = 'medium';
settings.aiDelay = 400;
settings.cornerBonus = 10;
} else if (level <= 35) {
// Levels 21-35: Hard AI with better strategy
settings.aiType = 'hard';
settings.aiDelay = 300;
settings.cornerBonus = 20;
settings.edgeBonus = 5;
settings.mobilityWeight = 5;
} else {
// Levels 36-50: Expert AI with advanced strategy
settings.aiType = 'expert';
settings.aiDelay = 200;
settings.cornerBonus = 30;
settings.edgeBonus = 10;
settings.mobilityWeight = 10;
}
// Adjust starting pieces based on level
if (level <= 15) {
settings.startingPieces = 4; // 2x2 center
} else if (level <= 30) {
settings.startingPieces = 6; // 2x3 center
} else {
settings.startingPieces = 8; // 2x4 center - more challenging start
}
return settings;
}
function selectLevel(level) {
selectedLevel = level;
currentDifficulty = getDifficultySettings(level);
// Update all buttons - deselect all first
for (var i = 0; i < levelButtons.length; i++) {
levelButtons[i].setSelected(false);
}
// Select the chosen level button
if (level >= 1 && level <= 50) {
levelButtons[level - 1].setSelected(true);
}
// Show start button
if (levelStartButton) {
levelStartButton.visible = true;
}
}
function initializeGame() {
// Create board container
boardContainer = new Container();
boardContainer.x = 1024;
boardContainer.y = 1366;
game.addChild(boardContainer);
// Create board background
var boardBg = LK.getAsset('boardBg', {
anchorX: 0.5,
anchorY: 0.5
});
boardContainer.addChild(boardBg);
// Create piece count texts
orangeCountText = new Text2('Orange: 2', {
size: 80,
fill: 0xFF8C00
});
orangeCountText.anchor.set(0, 0.5);
orangeCountText.x = -900;
orangeCountText.y = 1000;
boardContainer.addChild(orangeCountText);
greyCountText = new Text2('Grey: 2', {
size: 80,
fill: 0x808080
});
greyCountText.anchor.set(1, 0.5);
greyCountText.x = 900;
greyCountText.y = 1000;
boardContainer.addChild(greyCountText);
// Initialize board array and create cells
var cellSpacing = 1850 / BOARD_SIZE;
for (var row = 0; row < BOARD_SIZE; row++) {
board[row] = [];
cells[row] = [];
for (var col = 0; col < BOARD_SIZE; col++) {
board[row][col] = null;
var cell = new Cell();
cell.row = row;
cell.col = col;
cell.x = (col - 4.5) * cellSpacing;
cell.y = (row - 4.5) * cellSpacing;
boardContainer.addChild(cell);
cells[row][col] = cell;
}
}
// Create coordinate labels
var cellSpacing = 1850 / BOARD_SIZE;
for (var i = 0; i < BOARD_SIZE; i++) {
// Column labels (A-J)
var colLabel = new Text2(letters[i], {
size: 40,
fill: 0xFFFFFF
});
colLabel.anchor.set(0.5, 0.5);
colLabel.x = (i - 4.5) * cellSpacing;
colLabel.y = -945;
boardContainer.addChild(colLabel);
// Row labels (1-10)
var rowLabel = new Text2((i + 1).toString(), {
size: 40,
fill: 0xFFFFFF
});
rowLabel.anchor.set(0.5, 0.5);
rowLabel.x = -945;
rowLabel.y = (i - 4.5) * cellSpacing;
boardContainer.addChild(rowLabel);
}
// Create current player indicator
playerText = new Text2('Current Player: Orange', {
size: 60,
fill: 0xFF8C00
});
playerText.anchor.set(0.5, 0);
playerText.y = 100;
LK.gui.top.addChild(playerText);
// Create coordinates button
coordButton = new CoordButton();
coordButton.x = -100;
coordButton.y = 100;
LK.gui.topRight.addChild(coordButton);
// Create history panel (initially hidden)
historyPanel = new HistoryPanel();
historyPanel.visible = false;
game.addChild(historyPanel);
historyPanel.x = 1024;
historyPanel.y = 1366;
// Initialize with starting pieces based on difficulty
setupMode = true;
var difficulty = getDifficultySettings(selectedLevel);
if (difficulty.startingPieces === 4) {
// Standard 2x2 center
placePiece(4, 4);
placePiece(4, 5);
placePiece(5, 5);
placePiece(5, 4);
} else if (difficulty.startingPieces === 6) {
// 2x3 center setup - more challenging
placePiece(4, 4);
placePiece(4, 5);
placePiece(5, 4);
placePiece(5, 5);
placePiece(4, 6);
placePiece(5, 6);
} else if (difficulty.startingPieces === 8) {
// 2x4 center setup - most challenging
placePiece(4, 3);
placePiece(4, 4);
placePiece(4, 5);
placePiece(4, 6);
placePiece(5, 3);
placePiece(5, 4);
placePiece(5, 5);
placePiece(5, 6);
}
setupMode = false;
// Reset to orange player's turn
currentPlayer = 0;
updatePlayerText();
updatePieceCounts();
}
// Game functions
function getCoordinate(row, col) {
return letters[col] + (row + 1);
}
function placePiece(row, col) {
if (!gameStarted || board[row][col] !== null) return;
// Create and place piece
var piece = new Piece();
piece.setPlayer(currentPlayer);
cells[row][col].addChild(piece);
cells[row][col].piece = piece;
board[row][col] = currentPlayer;
// Play place sound
LK.getSound('place').play();
// Add to move history
moveHistory.push({
player: currentPlayer,
coord: getCoordinate(row, col)
});
// Update history panel if visible
if (historyVisible && historyPanel) {
historyPanel.updateHistory();
}
// Check for flips in all directions
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1],
// vertical and horizontal
[-1, -1], [-1, 1], [1, -1], [1, 1] // diagonals
];
var totalFlips = 0;
for (var d = 0; d < directions.length; d++) {
var flips = checkDirection(row, col, directions[d][0], directions[d][1]);
if (flips.length > 0) {
flipPieces(flips);
totalFlips += flips.length;
}
}
if (totalFlips > 0) {
LK.getSound('flip').play();
}
// Update piece counts
updatePieceCounts();
// Switch player
currentPlayer = 1 - currentPlayer;
updatePlayerText();
// Check for game over
checkGameOver();
// If it's now grey's turn (AI), make AI move
if (currentPlayer === 1 && !setupMode) {
makeAIMove();
}
}
function checkDirection(row, col, dRow, dCol) {
var flips = [];
var r = row + dRow;
var c = col + dCol;
// Find opponent pieces
while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE) {
if (board[r][c] === null) {
return [];
}
if (board[r][c] === currentPlayer) {
return flips;
}
flips.push([r, c]);
r += dRow;
c += dCol;
}
return [];
}
function flipPieces(positions) {
for (var i = 0; i < positions.length; i++) {
var row = positions[i][0];
var col = positions[i][1];
board[row][col] = currentPlayer;
cells[row][col].piece.flip();
}
}
function updatePlayerText() {
var player = currentPlayer === 0 ? 'Orange' : 'Grey';
var color = currentPlayer === 0 ? 0xff8c00 : 0x808080;
playerText.setText('Current Player: ' + player);
// Remove old text and create new one with updated color
LK.gui.top.removeChild(playerText);
playerText = new Text2('Current Player: ' + player, {
size: 60,
fill: color
});
playerText.anchor.set(0.5, 0);
playerText.y = 100;
LK.gui.top.addChild(playerText);
}
function updatePieceCounts() {
var orangeCount = 0;
var greyCount = 0;
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
if (board[row][col] === 0) {
orangeCount++;
} else if (board[row][col] === 1) {
greyCount++;
}
}
}
orangeCountText.setText('Orange: ' + orangeCount);
greyCountText.setText('Grey: ' + greyCount);
}
function checkGameOver() {
// Don't check for game over during initial setup
if (setupMode) return;
var hasEmpty = false;
var orangeCount = 0;
var greyCount = 0;
var hasValidMove = false;
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
if (board[row][col] === null) {
hasEmpty = true;
// Check if current player has valid move at this position
if (!hasValidMove && isValidMove(row, col, currentPlayer)) {
hasValidMove = true;
}
} else if (board[row][col] === 0) {
orangeCount++;
} else {
greyCount++;
}
}
}
// Check for game over conditions
if (!hasEmpty || !hasValidMove) {
if (orangeCount > greyCount) {
LK.showYouWin();
} else {
LK.showGameOver();
}
}
}
function toggleHistory() {
historyVisible = !historyVisible;
if (historyPanel) {
historyPanel.visible = historyVisible;
if (historyVisible) {
historyPanel.updateHistory();
}
}
}
// AI functions
function isValidMove(row, col, player) {
if (board[row][col] !== null) return false;
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var flips = checkDirectionForPlayer(row, col, directions[d][0], directions[d][1], player);
if (flips.length > 0) {
return true;
}
}
return false;
}
function checkDirectionForPlayer(row, col, dRow, dCol, player) {
var flips = [];
var r = row + dRow;
var c = col + dCol;
while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE) {
if (board[r][c] === null) {
return [];
}
if (board[r][c] === player) {
return flips;
}
flips.push([r, c]);
r += dRow;
c += dCol;
}
return [];
}
function evaluateMove(row, col, player) {
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]];
var totalFlips = 0;
for (var d = 0; d < directions.length; d++) {
var flips = checkDirectionForPlayer(row, col, directions[d][0], directions[d][1], player);
totalFlips += flips.length;
}
var difficulty = getDifficultySettings(selectedLevel);
var score = totalFlips;
// Apply strategic bonuses based on difficulty level
if (difficulty.aiType !== 'easy') {
// Corner bonus - corners are very valuable
if ((row === 0 || row === BOARD_SIZE - 1) && (col === 0 || col === BOARD_SIZE - 1)) {
score += difficulty.cornerBonus;
}
// Edge bonus - edges are somewhat valuable
else if (row === 0 || row === BOARD_SIZE - 1 || col === 0 || col === BOARD_SIZE - 1) {
score += difficulty.edgeBonus;
}
// Mobility consideration - how many moves will opponent have after this move
if (difficulty.mobilityWeight > 0) {
var opponentMoves = countValidMovesAfterMove(row, col, player);
score -= opponentMoves * difficulty.mobilityWeight;
}
// Avoid positions next to corners (they give opponent corner access)
if (difficulty.aiType === 'hard' || difficulty.aiType === 'expert') {
if (isNextToCorner(row, col)) {
score -= 15; // Penalty for moves next to corners
}
}
}
return score;
}
function countValidMovesAfterMove(row, col, player) {
// Simulate placing the piece and count opponent's valid moves
var originalValue = board[row][col];
board[row][col] = player;
var opponentPlayer = 1 - player;
var count = 0;
for (var r = 0; r < BOARD_SIZE; r++) {
for (var c = 0; c < BOARD_SIZE; c++) {
if (isValidMove(r, c, opponentPlayer)) {
count++;
}
}
}
board[row][col] = originalValue; // Restore original state
return count;
}
function isNextToCorner(row, col) {
// Check if position is adjacent to a corner (bad strategic move)
var corners = [[0, 0], [0, BOARD_SIZE - 1], [BOARD_SIZE - 1, 0], [BOARD_SIZE - 1, BOARD_SIZE - 1]];
for (var i = 0; i < corners.length; i++) {
var cornerRow = corners[i][0];
var cornerCol = corners[i][1];
var rowDiff = Math.abs(row - cornerRow);
var colDiff = Math.abs(col - cornerCol);
if (rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1 || rowDiff === 1 && colDiff === 1) {
// Only penalize if the corner is empty (don't avoid if corner is already taken)
if (board[cornerRow][cornerCol] === null) {
return true;
}
}
}
return false;
}
function makeAIMove() {
var validMoves = [];
var difficulty = getDifficultySettings(selectedLevel);
// Find all valid moves
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
if (isValidMove(row, col, 1)) {
validMoves.push({
row: row,
col: col,
score: evaluateMove(row, col, 1)
});
}
}
}
if (validMoves.length > 0) {
var chosenMove;
if (difficulty.aiType === 'easy') {
// Easy AI: pick randomly from moves that flip at least 1 piece
var movesWithFlips = [];
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].score > 0) {
movesWithFlips.push(validMoves[i]);
}
}
if (movesWithFlips.length === 0) {
movesWithFlips = validMoves;
}
var randomIndex = Math.floor(Math.random() * movesWithFlips.length);
chosenMove = movesWithFlips[randomIndex];
} else if (difficulty.aiType === 'medium') {
// Medium AI: pick from top 50% of moves
validMoves.sort(function (a, b) {
return b.score - a.score;
});
var topHalf = Math.ceil(validMoves.length / 2);
var randomIndex = Math.floor(Math.random() * topHalf);
chosenMove = validMoves[randomIndex];
} else if (difficulty.aiType === 'hard') {
// Hard AI: pick from top 25% of moves
validMoves.sort(function (a, b) {
return b.score - a.score;
});
var topQuarter = Math.max(1, Math.ceil(validMoves.length / 4));
var randomIndex = Math.floor(Math.random() * topQuarter);
chosenMove = validMoves[randomIndex];
} else {
// expert
// Expert AI: always pick the best move (with small randomness for variety)
validMoves.sort(function (a, b) {
return b.score - a.score;
});
var bestScore = validMoves[0].score;
var bestMoves = [];
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].score === bestScore) {
bestMoves.push(validMoves[i]);
} else {
break; // Stop when we leave the best moves
}
}
var randomIndex = Math.floor(Math.random() * bestMoves.length);
chosenMove = bestMoves[randomIndex];
}
// Add difficulty-based delay to make AI move visible
LK.setTimeout(function () {
placePiece(chosenMove.row, chosenMove.col);
}, difficulty.aiDelay);
}
}
;
Modern App Store icon, high definition, square with rounded corners, different othello game squares cells of wood, different wood, different colors, HD colors, for a game titled "Chorizora Othello" and without the description "A territorial strategy game where players flip opponent pieces by trapping them between their own pieces on a 10x10 grid.". with text on the middle of the icon "Chorizora Othello"!
Button "Coordinate". orange color In-Game asset. High contrast. No shadows. 3D