/****
* 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