/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cell = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.value = 0; // 0 = empty, 1 = X (blue), 2 = O (red)
var cellBg = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1
});
self.marker = null;
self.placeMarker = function (type) {
if (self.value !== 0) {
return false;
}
self.value = type;
if (type === 1) {
// Blue X
self.marker = self.attachAsset('xMarker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
});
tween(self.marker, {
alpha: 0.9,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Red O
self.marker = self.attachAsset('oMarker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
});
tween(self.marker, {
alpha: 0.9,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
LK.getSound('place').play();
return true;
};
self.down = function (x, y, obj) {
if (!gameReady || gameOver || self.value !== 0 || gameMode === 'menu') {
return;
}
// In AI mode, only allow player clicks during player's turn (currentPlayer === 1)
if (isAIMode && currentPlayer !== 1) {
return;
}
// Prevent rapid clicking by adding a small delay check
if (self.lastClickTime && Date.now() - self.lastClickTime < 100) {
return;
}
self.lastClickTime = Date.now();
// Store player's intended move before placing marker
var playerRow = self.row;
var playerCol = self.col;
// Mark this cell as reserved for player to prevent AI interference
self.isPlayerReserved = true;
if (self.placeMarker(currentPlayer)) {
// Clear reservation after successful placement
self.isPlayerReserved = false;
checkWin();
if (!gameOver) {
if (isAIMode && currentPlayer === 1) {
// AI mode - player played, now AI plays
currentPlayer = 2;
updateTurnDisplay();
makeAIMove();
} else if (isAIMode && currentPlayer === 2) {
// This shouldn't happen in AI mode since AI doesn't click
currentPlayer = 1;
updateTurnDisplay();
} else {
// Two player mode - switch players normally
currentPlayer = currentPlayer === 1 ? 2 : 1;
updateTurnDisplay();
}
}
} else {
// Clear reservation if placement failed
self.isPlayerReserved = false;
}
};
return self;
});
var WinLine = Container.expand(function () {
var self = Container.call(this);
self.line = self.attachAsset('winLine', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0
});
self.showWinLine = function (startX, startY, endX, endY, color) {
var deltaX = endX - startX;
var deltaY = endY - startY;
var angle = Math.atan2(deltaY, deltaX);
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
self.x = startX + deltaX / 2;
self.y = startY + deltaY / 2;
self.line.rotation = angle;
self.line.tint = color;
self.line.width = distance;
tween(self.line, {
alpha: 1,
scaleX: 1
}, {
duration: 500,
easing: tween.easeOut
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var grid = [];
var currentPlayer = 1; // 1 = Blue X, 2 = Red O
var gameOver = false;
var winLine = null;
var gameMode = 'menu'; // 'menu', 'ai', 'twoPlayer'
var isAIMode = false;
var currentLanguage = storage.language || 'tr'; // 'tr' for Turkish, 'en' for English
var aiDifficulty = storage.aiDifficulty || 'medium'; // 'easy', 'medium', 'hard'
var languageTexts = {
tr: {
blueTurn: 'Sıra: X\'in',
redTurn: 'Sıra: O\'nun',
blueWin: 'X KAZANDI!',
redWin: 'O KAZANDI!',
draw: 'BERABERE!',
playAgain: 'Tekrar Oyna',
language: 'Türkçe',
vsAI: 'Yapay Zeka ile Oyna',
twoPlayer: 'İki Kişilik Oyna',
selectMode: 'Oyun Modunu Seçin',
selectDifficulty: 'Zorluk Seviyesi Seçin',
easy: 'Kolay',
medium: 'Orta',
hard: 'Zor',
back: '← Geri',
exit: 'Çıkış'
},
en: {
blueTurn: 'Turn: X',
redTurn: 'Turn: O',
blueWin: 'X WON!',
redWin: 'O WON!',
draw: 'DRAW!',
playAgain: 'Play Again',
language: 'English',
vsAI: 'Play vs AI',
twoPlayer: 'Two Player',
selectMode: 'Select Game Mode',
selectDifficulty: 'Select Difficulty',
easy: 'Easy',
medium: 'Medium',
hard: 'Hard',
back: '← Back',
exit: 'Exit'
}
};
// Game setup
var gameBoard = game.addChild(new Container());
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2;
gameBoard.scaleX = 1.2;
gameBoard.scaleY = 1.2;
gameBoard.alpha = 0; // Hidden initially
// Create background
var background = gameBoard.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Create grid lines
var verticalLine1 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -300,
height: 1800
});
var verticalLine2 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
height: 1800
});
var horizontalLine1 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -300,
width: 1800,
height: 8
});
var horizontalLine2 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 300,
width: 1800,
height: 8
});
// Create cells
for (var row = 0; row < 3; row++) {
grid[row] = [];
for (var col = 0; col < 3; col++) {
var cell = gameBoard.addChild(new Cell(row, col));
cell.x = (col - 1) * 600;
cell.y = (row - 1) * 600;
grid[row][col] = cell;
}
}
// UI Elements
var blueTurnText = new Text2(languageTexts[currentLanguage].blueTurn, {
size: 120,
fill: 0x00AAFF
});
blueTurnText.anchor.set(0.5, 0);
LK.gui.top.addChild(blueTurnText);
blueTurnText.y = 150;
var redTurnText = new Text2(languageTexts[currentLanguage].redTurn, {
size: 120,
fill: 0xff0000
});
redTurnText.anchor.set(0.5, 0);
LK.gui.top.addChild(redTurnText);
redTurnText.y = 150;
var languageBtn = new Text2(languageTexts[currentLanguage === 'tr' ? 'en' : 'tr'].language, {
size: 60,
fill: 0xFFFFFF
});
languageBtn.anchor.set(0.5, 0);
LK.gui.top.addChild(languageBtn);
languageBtn.y = 80;
var statusText = new Text2('', {
size: 100,
fill: 0xFFFFFF
});
statusText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(statusText);
statusText.y = 400;
var playAgainBtn = new Text2(languageTexts[currentLanguage].playAgain, {
size: 80,
fill: 0xFFFFFF
});
playAgainBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(playAgainBtn);
playAgainBtn.y = 550;
playAgainBtn.alpha = 0;
// Menu UI elements
var menuTitle = new Text2(languageTexts[currentLanguage].selectMode, {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
LK.gui.center.addChild(menuTitle);
menuTitle.y = -200;
var vsAIBtn = new Text2(languageTexts[currentLanguage].vsAI, {
size: 100,
fill: 0x00AAFF
});
vsAIBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(vsAIBtn);
vsAIBtn.y = 0;
var twoPlayerBtn = new Text2(languageTexts[currentLanguage].twoPlayer, {
size: 100,
fill: 0xFF4444
});
twoPlayerBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(twoPlayerBtn);
twoPlayerBtn.y = 150;
var backBtn = new Text2(languageTexts[currentLanguage].back, {
size: 80,
fill: 0xFFFFFF
});
backBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(backBtn);
backBtn.x = -800;
backBtn.y = -300;
backBtn.alpha = 0;
var exitBtn = new Text2(languageTexts[currentLanguage].exit, {
size: 80,
fill: 0xFF0000
});
exitBtn.anchor.set(0.5, 0);
LK.gui.top.addChild(exitBtn);
exitBtn.y = 80;
exitBtn.x = 450;
exitBtn.alpha = 0;
// Difficulty selection UI
var difficultyTitle = new Text2(languageTexts[currentLanguage].selectDifficulty, {
size: 100,
fill: 0xFFFFFF
});
difficultyTitle.anchor.set(0.5, 0.5);
LK.gui.center.addChild(difficultyTitle);
difficultyTitle.y = -200;
difficultyTitle.alpha = 0;
var easyBtn = new Text2(languageTexts[currentLanguage].easy, {
size: 90,
fill: 0x00FF00
});
easyBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(easyBtn);
easyBtn.y = -50;
easyBtn.alpha = 0;
var mediumBtn = new Text2(languageTexts[currentLanguage].medium, {
size: 90,
fill: 0xFFAA00
});
mediumBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(mediumBtn);
mediumBtn.y = 50;
mediumBtn.alpha = 0;
var hardBtn = new Text2(languageTexts[currentLanguage].hard, {
size: 90,
fill: 0xFF0000
});
hardBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(hardBtn);
hardBtn.y = 150;
hardBtn.alpha = 0;
var currentDifficultyText = new Text2('', {
size: 60,
fill: 0xAAAAA
});
currentDifficultyText.anchor.set(0.5, 0);
LK.gui.top.addChild(currentDifficultyText);
currentDifficultyText.y = 200;
currentDifficultyText.alpha = 0;
// Language toggle functionality
languageBtn.down = function (x, y, obj) {
// Always allow language switching regardless of game state
currentLanguage = currentLanguage === 'tr' ? 'en' : 'tr';
storage.language = currentLanguage;
updateLanguageTexts();
};
// Menu button handlers
vsAIBtn.down = function (x, y, obj) {
if (gameMode === 'menu') {
showDifficultySelection();
}
};
// Difficulty button handlers
easyBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
aiDifficulty = 'easy';
storage.aiDifficulty = aiDifficulty;
startGame('ai');
}
};
mediumBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
aiDifficulty = 'medium';
storage.aiDifficulty = aiDifficulty;
startGame('ai');
}
};
hardBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
aiDifficulty = 'hard';
storage.aiDifficulty = aiDifficulty;
startGame('ai');
}
};
twoPlayerBtn.down = function (x, y, obj) {
if (gameMode === 'menu') {
startGame('twoPlayer');
}
};
backBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
showMenu();
} else if (gameMode !== 'menu') {
resetGame();
showMenu();
}
};
exitBtn.down = function (x, y, obj) {
if (gameMode !== 'menu') {
resetGame();
showMenu();
}
};
function updateLanguageTexts() {
blueTurnText.setText(languageTexts[currentLanguage].blueTurn);
redTurnText.setText(languageTexts[currentLanguage].redTurn);
playAgainBtn.setText(languageTexts[currentLanguage].playAgain);
languageBtn.setText(languageTexts[currentLanguage === 'tr' ? 'en' : 'tr'].language);
menuTitle.setText(languageTexts[currentLanguage].selectMode);
vsAIBtn.setText(languageTexts[currentLanguage].vsAI);
twoPlayerBtn.setText(languageTexts[currentLanguage].twoPlayer);
backBtn.setText(languageTexts[currentLanguage].back);
exitBtn.setText(languageTexts[currentLanguage].exit);
difficultyTitle.setText(languageTexts[currentLanguage].selectDifficulty);
easyBtn.setText(languageTexts[currentLanguage].easy);
mediumBtn.setText(languageTexts[currentLanguage].medium);
hardBtn.setText(languageTexts[currentLanguage].hard);
updateDifficultyDisplay();
}
// Start in menu mode
showMenu();
function updateTurnDisplay() {
if (gameMode === 'menu' || gameOver) {
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
} else if (currentPlayer === 1) {
blueTurnText.alpha = 1;
redTurnText.alpha = 0;
} else {
blueTurnText.alpha = 0;
redTurnText.alpha = 1;
}
}
function showMenu() {
gameMode = 'menu';
gameBoard.alpha = 0;
menuTitle.alpha = 1;
vsAIBtn.alpha = 1;
twoPlayerBtn.alpha = 1;
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
statusText.alpha = 0;
playAgainBtn.alpha = 0;
backBtn.alpha = 0;
exitBtn.alpha = 0;
difficultyTitle.alpha = 0;
easyBtn.alpha = 0;
mediumBtn.alpha = 0;
hardBtn.alpha = 0;
currentDifficultyText.alpha = 0;
}
function showDifficultySelection() {
gameMode = 'difficulty';
menuTitle.alpha = 0;
vsAIBtn.alpha = 0;
twoPlayerBtn.alpha = 0;
difficultyTitle.alpha = 1;
easyBtn.alpha = 1;
mediumBtn.alpha = 1;
hardBtn.alpha = 1;
backBtn.alpha = 1;
updateDifficultyDisplay();
}
function updateDifficultyDisplay() {
var difficultyText = languageTexts[currentLanguage === 'tr' ? 'tr' : 'en'][aiDifficulty];
easyBtn.fill = aiDifficulty === 'easy' ? 0x00FF88 : 0x00FF00;
mediumBtn.fill = aiDifficulty === 'medium' ? 0xFFCC44 : 0xFFAA00;
hardBtn.fill = aiDifficulty === 'hard' ? 0xFF4444 : 0xFF0000;
}
function startGame(mode) {
gameMode = mode;
isAIMode = mode === 'ai';
gameBoard.alpha = 1;
menuTitle.alpha = 0;
vsAIBtn.alpha = 0;
twoPlayerBtn.alpha = 0;
difficultyTitle.alpha = 0;
easyBtn.alpha = 0;
mediumBtn.alpha = 0;
hardBtn.alpha = 0;
backBtn.alpha = 1;
exitBtn.alpha = 1;
// Show current difficulty in AI mode
if (isAIMode) {
currentDifficultyText.setText(languageTexts[currentLanguage][aiDifficulty]);
currentDifficultyText.alpha = 1;
currentPlayer = 1;
} else {
currentDifficultyText.alpha = 0;
// In two player mode, random start
currentPlayer = Math.random() < 0.5 ? 1 : 2;
}
updateTurnDisplay();
}
function checkWin() {
var winner = 0;
var winPositions = [];
// Check rows
for (var row = 0; row < 3; row++) {
if (grid[row][0].value !== 0 && grid[row][0].value === grid[row][1].value && grid[row][1].value === grid[row][2].value) {
winner = grid[row][0].value;
winPositions = [{
row: row,
col: 0
}, {
row: row,
col: 1
}, {
row: row,
col: 2
}];
break;
}
}
// Check columns
if (winner === 0) {
for (var col = 0; col < 3; col++) {
if (grid[0][col].value !== 0 && grid[0][col].value === grid[1][col].value && grid[1][col].value === grid[2][col].value) {
winner = grid[0][col].value;
winPositions = [{
row: 0,
col: col
}, {
row: 1,
col: col
}, {
row: 2,
col: col
}];
break;
}
}
}
// Check diagonals
if (winner === 0) {
if (grid[0][0].value !== 0 && grid[0][0].value === grid[1][1].value && grid[1][1].value === grid[2][2].value) {
winner = grid[0][0].value;
winPositions = [{
row: 0,
col: 0
}, {
row: 1,
col: 1
}, {
row: 2,
col: 2
}];
} else if (grid[0][2].value !== 0 && grid[0][2].value === grid[1][1].value && grid[1][1].value === grid[2][0].value) {
winner = grid[0][2].value;
winPositions = [{
row: 0,
col: 2
}, {
row: 1,
col: 1
}, {
row: 2,
col: 0
}];
}
}
if (winner !== 0) {
gameOver = true;
showWinner(winner, winPositions);
return;
}
// Check for draw
var isDraw = true;
for (var r = 0; r < 3; r++) {
for (var c = 0; c < 3; c++) {
if (grid[r][c].value === 0) {
isDraw = false;
break;
}
}
if (!isDraw) {
break;
}
}
if (isDraw) {
gameOver = true;
showDraw();
}
}
function showWinner(winner, positions) {
LK.getSound('win').play();
var winnerText = winner === 1 ? languageTexts[currentLanguage].blueWin : languageTexts[currentLanguage].redWin;
var winnerColor = 0x000000; // Black color
statusText.setText(winnerText);
statusText.fill = winnerColor;
statusText.alpha = 1;
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
// Show win line
winLine = gameBoard.addChild(new WinLine());
var startPos = grid[positions[0].row][positions[0].col];
var endPos = grid[positions[2].row][positions[2].col];
var lineColor = winner === 1 ? 0x00aaff : 0xff4444;
winLine.showWinLine(startPos.x, startPos.y, endPos.x, endPos.y, lineColor);
// Show play again button
tween(playAgainBtn, {
alpha: 1
}, {
duration: 500
});
}
function showDraw() {
statusText.setText(languageTexts[currentLanguage].draw);
statusText.fill = 0x000000; // Black color
statusText.alpha = 1;
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
// Show play again button
tween(playAgainBtn, {
alpha: 1
}, {
duration: 500
});
}
function makeAIMove() {
if (gameOver) {
return;
}
// Use setTimeout to yield frame and prevent blocking
LK.setTimeout(function () {
// Simple AI strategy: try to win, block player, or play randomly
var bestMove = findBestMove();
if (bestMove && bestMove.value === 0 && !bestMove.isPlayerReserved) {
// Double check the cell is still valid before AI places marker
if (bestMove.placeMarker(2)) {
checkWin();
if (!gameOver) {
currentPlayer = 1;
updateTurnDisplay();
}
}
}
}, 800); // Increased delay to slow down AI response
}
function findBestMove() {
var emptyCells = [];
// Collect empty cells
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var cell = grid[row][col];
if (cell.value === 0 && !cell.isPlayerReserved) {
emptyCells.push(cell);
}
}
}
// Easy difficulty: mostly random with some winning moves
if (aiDifficulty === 'easy') {
// 30% chance to play optimally, 70% random
if (Math.random() < 0.3) {
return findOptimalMove(emptyCells);
} else {
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
}
// Medium difficulty: balanced strategy
if (aiDifficulty === 'medium') {
// 70% chance to play optimally, 30% random
if (Math.random() < 0.7) {
return findOptimalMove(emptyCells);
} else {
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
}
// Hard difficulty: always optimal
return findOptimalMove(emptyCells);
}
function findOptimalMove(emptyCells) {
// Check for winning move
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
cell.value = 2;
if (checkWinCondition(2)) {
cell.value = 0;
return cell;
}
cell.value = 0;
}
// Check for blocking move
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
if (cell.value === 0 && !cell.isPlayerReserved) {
cell.value = 1;
if (checkWinCondition(1)) {
cell.value = 0;
return cell;
}
cell.value = 0;
}
}
// Play center if available
if (grid[1][1].value === 0 && !grid[1][1].isPlayerReserved) {
return grid[1][1];
}
// Play corners
var corners = [grid[0][0], grid[0][2], grid[2][0], grid[2][2]];
for (var i = 0; i < corners.length; i++) {
if (corners[i].value === 0 && !corners[i].isPlayerReserved) {
return corners[i];
}
}
// Play any available cell
var validCells = [];
for (var i = 0; i < emptyCells.length; i++) {
if (!emptyCells[i].isPlayerReserved) {
validCells.push(emptyCells[i]);
}
}
if (validCells.length > 0) {
return validCells[Math.floor(Math.random() * validCells.length)];
}
return null;
}
function checkWinCondition(player) {
// Check rows
for (var row = 0; row < 3; row++) {
if (grid[row][0].value === player && grid[row][1].value === player && grid[row][2].value === player) {
return true;
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (grid[0][col].value === player && grid[1][col].value === player && grid[2][col].value === player) {
return true;
}
}
// Check diagonals
if (grid[0][0].value === player && grid[1][1].value === player && grid[2][2].value === player) {
return true;
}
if (grid[0][2].value === player && grid[1][1].value === player && grid[2][0].value === player) {
return true;
}
return false;
}
function resetGame() {
gameOver = false;
// Start according to game mode
if (gameMode === 'ai') {
currentPlayer = 1; // Player always starts in AI mode
isAIMode = true;
} else if (gameMode === 'twoPlayer') {
currentPlayer = Math.random() < 0.5 ? 1 : 2;
isAIMode = false;
} else {
// Menu mode
currentPlayer = 1;
isAIMode = false;
}
// Clear grid
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var cell = grid[row][col];
cell.value = 0;
if (cell.marker) {
cell.marker.destroy();
cell.marker = null;
}
}
}
// Clear UI
statusText.setText('');
statusText.alpha = 0;
playAgainBtn.alpha = 0;
// Remove win line
if (winLine) {
// Reset line properties before destroying
winLine.line.alpha = 0;
winLine.line.scaleX = 0;
winLine.destroy();
winLine = null;
}
updateTurnDisplay();
}
// Save current version function
function saveCurrentVersion() {
storage.savedVersion = {
timestamp: Date.now(),
version: 'TicTacToe_TurkishEnglish_v1.0'
};
}
// Play again button handler
playAgainBtn.down = function (x, y, obj) {
if (gameOver) {
resetGame();
}
};
// Save the current version on game initialization
saveCurrentVersion();
// Play background music continuously looping
LK.playMusic('325');
// Add small delay to prevent initial freeze when entering game
var gameReady = false;
LK.setTimeout(function () {
gameReady = true;
}, 50); // Reduced delay for better responsiveness; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cell = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.value = 0; // 0 = empty, 1 = X (blue), 2 = O (red)
var cellBg = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1
});
self.marker = null;
self.placeMarker = function (type) {
if (self.value !== 0) {
return false;
}
self.value = type;
if (type === 1) {
// Blue X
self.marker = self.attachAsset('xMarker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
});
tween(self.marker, {
alpha: 0.9,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Red O
self.marker = self.attachAsset('oMarker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
});
tween(self.marker, {
alpha: 0.9,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
LK.getSound('place').play();
return true;
};
self.down = function (x, y, obj) {
if (!gameReady || gameOver || self.value !== 0 || gameMode === 'menu') {
return;
}
// In AI mode, only allow player clicks during player's turn (currentPlayer === 1)
if (isAIMode && currentPlayer !== 1) {
return;
}
// Prevent rapid clicking by adding a small delay check
if (self.lastClickTime && Date.now() - self.lastClickTime < 100) {
return;
}
self.lastClickTime = Date.now();
// Store player's intended move before placing marker
var playerRow = self.row;
var playerCol = self.col;
// Mark this cell as reserved for player to prevent AI interference
self.isPlayerReserved = true;
if (self.placeMarker(currentPlayer)) {
// Clear reservation after successful placement
self.isPlayerReserved = false;
checkWin();
if (!gameOver) {
if (isAIMode && currentPlayer === 1) {
// AI mode - player played, now AI plays
currentPlayer = 2;
updateTurnDisplay();
makeAIMove();
} else if (isAIMode && currentPlayer === 2) {
// This shouldn't happen in AI mode since AI doesn't click
currentPlayer = 1;
updateTurnDisplay();
} else {
// Two player mode - switch players normally
currentPlayer = currentPlayer === 1 ? 2 : 1;
updateTurnDisplay();
}
}
} else {
// Clear reservation if placement failed
self.isPlayerReserved = false;
}
};
return self;
});
var WinLine = Container.expand(function () {
var self = Container.call(this);
self.line = self.attachAsset('winLine', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0
});
self.showWinLine = function (startX, startY, endX, endY, color) {
var deltaX = endX - startX;
var deltaY = endY - startY;
var angle = Math.atan2(deltaY, deltaX);
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
self.x = startX + deltaX / 2;
self.y = startY + deltaY / 2;
self.line.rotation = angle;
self.line.tint = color;
self.line.width = distance;
tween(self.line, {
alpha: 1,
scaleX: 1
}, {
duration: 500,
easing: tween.easeOut
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var grid = [];
var currentPlayer = 1; // 1 = Blue X, 2 = Red O
var gameOver = false;
var winLine = null;
var gameMode = 'menu'; // 'menu', 'ai', 'twoPlayer'
var isAIMode = false;
var currentLanguage = storage.language || 'tr'; // 'tr' for Turkish, 'en' for English
var aiDifficulty = storage.aiDifficulty || 'medium'; // 'easy', 'medium', 'hard'
var languageTexts = {
tr: {
blueTurn: 'Sıra: X\'in',
redTurn: 'Sıra: O\'nun',
blueWin: 'X KAZANDI!',
redWin: 'O KAZANDI!',
draw: 'BERABERE!',
playAgain: 'Tekrar Oyna',
language: 'Türkçe',
vsAI: 'Yapay Zeka ile Oyna',
twoPlayer: 'İki Kişilik Oyna',
selectMode: 'Oyun Modunu Seçin',
selectDifficulty: 'Zorluk Seviyesi Seçin',
easy: 'Kolay',
medium: 'Orta',
hard: 'Zor',
back: '← Geri',
exit: 'Çıkış'
},
en: {
blueTurn: 'Turn: X',
redTurn: 'Turn: O',
blueWin: 'X WON!',
redWin: 'O WON!',
draw: 'DRAW!',
playAgain: 'Play Again',
language: 'English',
vsAI: 'Play vs AI',
twoPlayer: 'Two Player',
selectMode: 'Select Game Mode',
selectDifficulty: 'Select Difficulty',
easy: 'Easy',
medium: 'Medium',
hard: 'Hard',
back: '← Back',
exit: 'Exit'
}
};
// Game setup
var gameBoard = game.addChild(new Container());
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2;
gameBoard.scaleX = 1.2;
gameBoard.scaleY = 1.2;
gameBoard.alpha = 0; // Hidden initially
// Create background
var background = gameBoard.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Create grid lines
var verticalLine1 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -300,
height: 1800
});
var verticalLine2 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
height: 1800
});
var horizontalLine1 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -300,
width: 1800,
height: 8
});
var horizontalLine2 = gameBoard.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 300,
width: 1800,
height: 8
});
// Create cells
for (var row = 0; row < 3; row++) {
grid[row] = [];
for (var col = 0; col < 3; col++) {
var cell = gameBoard.addChild(new Cell(row, col));
cell.x = (col - 1) * 600;
cell.y = (row - 1) * 600;
grid[row][col] = cell;
}
}
// UI Elements
var blueTurnText = new Text2(languageTexts[currentLanguage].blueTurn, {
size: 120,
fill: 0x00AAFF
});
blueTurnText.anchor.set(0.5, 0);
LK.gui.top.addChild(blueTurnText);
blueTurnText.y = 150;
var redTurnText = new Text2(languageTexts[currentLanguage].redTurn, {
size: 120,
fill: 0xff0000
});
redTurnText.anchor.set(0.5, 0);
LK.gui.top.addChild(redTurnText);
redTurnText.y = 150;
var languageBtn = new Text2(languageTexts[currentLanguage === 'tr' ? 'en' : 'tr'].language, {
size: 60,
fill: 0xFFFFFF
});
languageBtn.anchor.set(0.5, 0);
LK.gui.top.addChild(languageBtn);
languageBtn.y = 80;
var statusText = new Text2('', {
size: 100,
fill: 0xFFFFFF
});
statusText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(statusText);
statusText.y = 400;
var playAgainBtn = new Text2(languageTexts[currentLanguage].playAgain, {
size: 80,
fill: 0xFFFFFF
});
playAgainBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(playAgainBtn);
playAgainBtn.y = 550;
playAgainBtn.alpha = 0;
// Menu UI elements
var menuTitle = new Text2(languageTexts[currentLanguage].selectMode, {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
LK.gui.center.addChild(menuTitle);
menuTitle.y = -200;
var vsAIBtn = new Text2(languageTexts[currentLanguage].vsAI, {
size: 100,
fill: 0x00AAFF
});
vsAIBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(vsAIBtn);
vsAIBtn.y = 0;
var twoPlayerBtn = new Text2(languageTexts[currentLanguage].twoPlayer, {
size: 100,
fill: 0xFF4444
});
twoPlayerBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(twoPlayerBtn);
twoPlayerBtn.y = 150;
var backBtn = new Text2(languageTexts[currentLanguage].back, {
size: 80,
fill: 0xFFFFFF
});
backBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(backBtn);
backBtn.x = -800;
backBtn.y = -300;
backBtn.alpha = 0;
var exitBtn = new Text2(languageTexts[currentLanguage].exit, {
size: 80,
fill: 0xFF0000
});
exitBtn.anchor.set(0.5, 0);
LK.gui.top.addChild(exitBtn);
exitBtn.y = 80;
exitBtn.x = 450;
exitBtn.alpha = 0;
// Difficulty selection UI
var difficultyTitle = new Text2(languageTexts[currentLanguage].selectDifficulty, {
size: 100,
fill: 0xFFFFFF
});
difficultyTitle.anchor.set(0.5, 0.5);
LK.gui.center.addChild(difficultyTitle);
difficultyTitle.y = -200;
difficultyTitle.alpha = 0;
var easyBtn = new Text2(languageTexts[currentLanguage].easy, {
size: 90,
fill: 0x00FF00
});
easyBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(easyBtn);
easyBtn.y = -50;
easyBtn.alpha = 0;
var mediumBtn = new Text2(languageTexts[currentLanguage].medium, {
size: 90,
fill: 0xFFAA00
});
mediumBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(mediumBtn);
mediumBtn.y = 50;
mediumBtn.alpha = 0;
var hardBtn = new Text2(languageTexts[currentLanguage].hard, {
size: 90,
fill: 0xFF0000
});
hardBtn.anchor.set(0.5, 0.5);
LK.gui.center.addChild(hardBtn);
hardBtn.y = 150;
hardBtn.alpha = 0;
var currentDifficultyText = new Text2('', {
size: 60,
fill: 0xAAAAA
});
currentDifficultyText.anchor.set(0.5, 0);
LK.gui.top.addChild(currentDifficultyText);
currentDifficultyText.y = 200;
currentDifficultyText.alpha = 0;
// Language toggle functionality
languageBtn.down = function (x, y, obj) {
// Always allow language switching regardless of game state
currentLanguage = currentLanguage === 'tr' ? 'en' : 'tr';
storage.language = currentLanguage;
updateLanguageTexts();
};
// Menu button handlers
vsAIBtn.down = function (x, y, obj) {
if (gameMode === 'menu') {
showDifficultySelection();
}
};
// Difficulty button handlers
easyBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
aiDifficulty = 'easy';
storage.aiDifficulty = aiDifficulty;
startGame('ai');
}
};
mediumBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
aiDifficulty = 'medium';
storage.aiDifficulty = aiDifficulty;
startGame('ai');
}
};
hardBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
aiDifficulty = 'hard';
storage.aiDifficulty = aiDifficulty;
startGame('ai');
}
};
twoPlayerBtn.down = function (x, y, obj) {
if (gameMode === 'menu') {
startGame('twoPlayer');
}
};
backBtn.down = function (x, y, obj) {
if (gameMode === 'difficulty') {
showMenu();
} else if (gameMode !== 'menu') {
resetGame();
showMenu();
}
};
exitBtn.down = function (x, y, obj) {
if (gameMode !== 'menu') {
resetGame();
showMenu();
}
};
function updateLanguageTexts() {
blueTurnText.setText(languageTexts[currentLanguage].blueTurn);
redTurnText.setText(languageTexts[currentLanguage].redTurn);
playAgainBtn.setText(languageTexts[currentLanguage].playAgain);
languageBtn.setText(languageTexts[currentLanguage === 'tr' ? 'en' : 'tr'].language);
menuTitle.setText(languageTexts[currentLanguage].selectMode);
vsAIBtn.setText(languageTexts[currentLanguage].vsAI);
twoPlayerBtn.setText(languageTexts[currentLanguage].twoPlayer);
backBtn.setText(languageTexts[currentLanguage].back);
exitBtn.setText(languageTexts[currentLanguage].exit);
difficultyTitle.setText(languageTexts[currentLanguage].selectDifficulty);
easyBtn.setText(languageTexts[currentLanguage].easy);
mediumBtn.setText(languageTexts[currentLanguage].medium);
hardBtn.setText(languageTexts[currentLanguage].hard);
updateDifficultyDisplay();
}
// Start in menu mode
showMenu();
function updateTurnDisplay() {
if (gameMode === 'menu' || gameOver) {
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
} else if (currentPlayer === 1) {
blueTurnText.alpha = 1;
redTurnText.alpha = 0;
} else {
blueTurnText.alpha = 0;
redTurnText.alpha = 1;
}
}
function showMenu() {
gameMode = 'menu';
gameBoard.alpha = 0;
menuTitle.alpha = 1;
vsAIBtn.alpha = 1;
twoPlayerBtn.alpha = 1;
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
statusText.alpha = 0;
playAgainBtn.alpha = 0;
backBtn.alpha = 0;
exitBtn.alpha = 0;
difficultyTitle.alpha = 0;
easyBtn.alpha = 0;
mediumBtn.alpha = 0;
hardBtn.alpha = 0;
currentDifficultyText.alpha = 0;
}
function showDifficultySelection() {
gameMode = 'difficulty';
menuTitle.alpha = 0;
vsAIBtn.alpha = 0;
twoPlayerBtn.alpha = 0;
difficultyTitle.alpha = 1;
easyBtn.alpha = 1;
mediumBtn.alpha = 1;
hardBtn.alpha = 1;
backBtn.alpha = 1;
updateDifficultyDisplay();
}
function updateDifficultyDisplay() {
var difficultyText = languageTexts[currentLanguage === 'tr' ? 'tr' : 'en'][aiDifficulty];
easyBtn.fill = aiDifficulty === 'easy' ? 0x00FF88 : 0x00FF00;
mediumBtn.fill = aiDifficulty === 'medium' ? 0xFFCC44 : 0xFFAA00;
hardBtn.fill = aiDifficulty === 'hard' ? 0xFF4444 : 0xFF0000;
}
function startGame(mode) {
gameMode = mode;
isAIMode = mode === 'ai';
gameBoard.alpha = 1;
menuTitle.alpha = 0;
vsAIBtn.alpha = 0;
twoPlayerBtn.alpha = 0;
difficultyTitle.alpha = 0;
easyBtn.alpha = 0;
mediumBtn.alpha = 0;
hardBtn.alpha = 0;
backBtn.alpha = 1;
exitBtn.alpha = 1;
// Show current difficulty in AI mode
if (isAIMode) {
currentDifficultyText.setText(languageTexts[currentLanguage][aiDifficulty]);
currentDifficultyText.alpha = 1;
currentPlayer = 1;
} else {
currentDifficultyText.alpha = 0;
// In two player mode, random start
currentPlayer = Math.random() < 0.5 ? 1 : 2;
}
updateTurnDisplay();
}
function checkWin() {
var winner = 0;
var winPositions = [];
// Check rows
for (var row = 0; row < 3; row++) {
if (grid[row][0].value !== 0 && grid[row][0].value === grid[row][1].value && grid[row][1].value === grid[row][2].value) {
winner = grid[row][0].value;
winPositions = [{
row: row,
col: 0
}, {
row: row,
col: 1
}, {
row: row,
col: 2
}];
break;
}
}
// Check columns
if (winner === 0) {
for (var col = 0; col < 3; col++) {
if (grid[0][col].value !== 0 && grid[0][col].value === grid[1][col].value && grid[1][col].value === grid[2][col].value) {
winner = grid[0][col].value;
winPositions = [{
row: 0,
col: col
}, {
row: 1,
col: col
}, {
row: 2,
col: col
}];
break;
}
}
}
// Check diagonals
if (winner === 0) {
if (grid[0][0].value !== 0 && grid[0][0].value === grid[1][1].value && grid[1][1].value === grid[2][2].value) {
winner = grid[0][0].value;
winPositions = [{
row: 0,
col: 0
}, {
row: 1,
col: 1
}, {
row: 2,
col: 2
}];
} else if (grid[0][2].value !== 0 && grid[0][2].value === grid[1][1].value && grid[1][1].value === grid[2][0].value) {
winner = grid[0][2].value;
winPositions = [{
row: 0,
col: 2
}, {
row: 1,
col: 1
}, {
row: 2,
col: 0
}];
}
}
if (winner !== 0) {
gameOver = true;
showWinner(winner, winPositions);
return;
}
// Check for draw
var isDraw = true;
for (var r = 0; r < 3; r++) {
for (var c = 0; c < 3; c++) {
if (grid[r][c].value === 0) {
isDraw = false;
break;
}
}
if (!isDraw) {
break;
}
}
if (isDraw) {
gameOver = true;
showDraw();
}
}
function showWinner(winner, positions) {
LK.getSound('win').play();
var winnerText = winner === 1 ? languageTexts[currentLanguage].blueWin : languageTexts[currentLanguage].redWin;
var winnerColor = 0x000000; // Black color
statusText.setText(winnerText);
statusText.fill = winnerColor;
statusText.alpha = 1;
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
// Show win line
winLine = gameBoard.addChild(new WinLine());
var startPos = grid[positions[0].row][positions[0].col];
var endPos = grid[positions[2].row][positions[2].col];
var lineColor = winner === 1 ? 0x00aaff : 0xff4444;
winLine.showWinLine(startPos.x, startPos.y, endPos.x, endPos.y, lineColor);
// Show play again button
tween(playAgainBtn, {
alpha: 1
}, {
duration: 500
});
}
function showDraw() {
statusText.setText(languageTexts[currentLanguage].draw);
statusText.fill = 0x000000; // Black color
statusText.alpha = 1;
blueTurnText.alpha = 0;
redTurnText.alpha = 0;
// Show play again button
tween(playAgainBtn, {
alpha: 1
}, {
duration: 500
});
}
function makeAIMove() {
if (gameOver) {
return;
}
// Use setTimeout to yield frame and prevent blocking
LK.setTimeout(function () {
// Simple AI strategy: try to win, block player, or play randomly
var bestMove = findBestMove();
if (bestMove && bestMove.value === 0 && !bestMove.isPlayerReserved) {
// Double check the cell is still valid before AI places marker
if (bestMove.placeMarker(2)) {
checkWin();
if (!gameOver) {
currentPlayer = 1;
updateTurnDisplay();
}
}
}
}, 800); // Increased delay to slow down AI response
}
function findBestMove() {
var emptyCells = [];
// Collect empty cells
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var cell = grid[row][col];
if (cell.value === 0 && !cell.isPlayerReserved) {
emptyCells.push(cell);
}
}
}
// Easy difficulty: mostly random with some winning moves
if (aiDifficulty === 'easy') {
// 30% chance to play optimally, 70% random
if (Math.random() < 0.3) {
return findOptimalMove(emptyCells);
} else {
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
}
// Medium difficulty: balanced strategy
if (aiDifficulty === 'medium') {
// 70% chance to play optimally, 30% random
if (Math.random() < 0.7) {
return findOptimalMove(emptyCells);
} else {
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
}
// Hard difficulty: always optimal
return findOptimalMove(emptyCells);
}
function findOptimalMove(emptyCells) {
// Check for winning move
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
cell.value = 2;
if (checkWinCondition(2)) {
cell.value = 0;
return cell;
}
cell.value = 0;
}
// Check for blocking move
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
if (cell.value === 0 && !cell.isPlayerReserved) {
cell.value = 1;
if (checkWinCondition(1)) {
cell.value = 0;
return cell;
}
cell.value = 0;
}
}
// Play center if available
if (grid[1][1].value === 0 && !grid[1][1].isPlayerReserved) {
return grid[1][1];
}
// Play corners
var corners = [grid[0][0], grid[0][2], grid[2][0], grid[2][2]];
for (var i = 0; i < corners.length; i++) {
if (corners[i].value === 0 && !corners[i].isPlayerReserved) {
return corners[i];
}
}
// Play any available cell
var validCells = [];
for (var i = 0; i < emptyCells.length; i++) {
if (!emptyCells[i].isPlayerReserved) {
validCells.push(emptyCells[i]);
}
}
if (validCells.length > 0) {
return validCells[Math.floor(Math.random() * validCells.length)];
}
return null;
}
function checkWinCondition(player) {
// Check rows
for (var row = 0; row < 3; row++) {
if (grid[row][0].value === player && grid[row][1].value === player && grid[row][2].value === player) {
return true;
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (grid[0][col].value === player && grid[1][col].value === player && grid[2][col].value === player) {
return true;
}
}
// Check diagonals
if (grid[0][0].value === player && grid[1][1].value === player && grid[2][2].value === player) {
return true;
}
if (grid[0][2].value === player && grid[1][1].value === player && grid[2][0].value === player) {
return true;
}
return false;
}
function resetGame() {
gameOver = false;
// Start according to game mode
if (gameMode === 'ai') {
currentPlayer = 1; // Player always starts in AI mode
isAIMode = true;
} else if (gameMode === 'twoPlayer') {
currentPlayer = Math.random() < 0.5 ? 1 : 2;
isAIMode = false;
} else {
// Menu mode
currentPlayer = 1;
isAIMode = false;
}
// Clear grid
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var cell = grid[row][col];
cell.value = 0;
if (cell.marker) {
cell.marker.destroy();
cell.marker = null;
}
}
}
// Clear UI
statusText.setText('');
statusText.alpha = 0;
playAgainBtn.alpha = 0;
// Remove win line
if (winLine) {
// Reset line properties before destroying
winLine.line.alpha = 0;
winLine.line.scaleX = 0;
winLine.destroy();
winLine = null;
}
updateTurnDisplay();
}
// Save current version function
function saveCurrentVersion() {
storage.savedVersion = {
timestamp: Date.now(),
version: 'TicTacToe_TurkishEnglish_v1.0'
};
}
// Play again button handler
playAgainBtn.down = function (x, y, obj) {
if (gameOver) {
resetGame();
}
};
// Save the current version on game initialization
saveCurrentVersion();
// Play background music continuously looping
LK.playMusic('325');
// Add small delay to prevent initial freeze when entering game
var gameReady = false;
LK.setTimeout(function () {
gameReady = true;
}, 50); // Reduced delay for better responsiveness;