User prompt
yapay zeka ile oyuna başladıktan sonra geri tuşunu kaldır
User prompt
geri tuşunu biraz daha sağ tarafa çek
User prompt
Geri tuşunu biraz aşağı çek
User prompt
Geri tuşunu dil değiştirme tuşunun sağ tarafının alt kısmına koy
User prompt
2 kişilik modunu seçince geri tuşu olmasın
User prompt
Geri tuşunu Zorluk modunun üstüne koy
User prompt
Kodu gözden geçir ve bazı teknik hataları düzelt.
User prompt
Normal mod yapay zekasını gözden geçir. Bazı hareketlerden sonra hamle yapmıyor. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Yapay zekanın belirli birkaç hareket yapıldıktan sonra hareket yani hamle yapmadığını tespit ettim hangi hamleyi yaparsak yapalım hamle yapsın. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Yapay zekanın her modunu kontrol et kolay,normal,zor
User prompt
Yapay zeka kendi sırası olmasına rağmen tıklamıyor.
User prompt
yapay zekadan önce tıklayabiliyoruz ve daha çok bu çizgiler üzerine hataları düzelt ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
şimdi tüm fark ettiğin hataları düzelt oyunu bozma ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Yapay zekanın üst üste 2 kere kullanma hatası var düzelt
User prompt
ya da kazanan takım için çıkan sesi kaldır
User prompt
kazanan takım için ingilizce şu takım kazandı gibi birşey desin
User prompt
Sən oyunumu nə yaptın? Niyə kamera izni ilə mikrofon izni var? Qaldır!
User prompt
Kazanan kişinin adını yani takımın rengini ve başka şeyini söyleyen bir ses ekle. ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Artık birşey tıkladığımızda animasyonlu olarak böyle yerine geçsin. Saadet stajı tıkladığımızda otomatik yerine geçmesin yani. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Oyuncunun yapay zekadan önce tıklamasını engelle
User prompt
mavi takım kazandı kırmızı takım kazandı yazan yazı dil değiştirsek bile değişmiyor
User prompt
Dili değiştirsek bile şu takım kazandığı yazan yazı dili değişmiyor.
User prompt
Tekrar oyna tuşu daha belirgin hale getir.
User prompt
Zor mod yapay zeka en iyi hamleleri yapmak için tüm olası senaryoları değerlendirir. Asla hata yapmaz. Genellikle beraber kalmayı veya kazanmayı hedefler. Böylece oyunu deneyimli oyuncular için büyük meydan okumak.
User prompt
Bu modumu seçmek istiyorsun diye uyarı veren menüden çıkamıyoruz, hayır veya evet'e tıklayamıyoruz.
/**** * 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.01 // Make almost invisible but still interactive }); // Make the entire cell area interactive self.interactive = true; self.hitArea = new Rectangle(-290, -290, 580, 580); self.marker = null; // Add hover effect to show clickable area self.move = function (x, y, obj) { if (!gameReady || gameOver || self.value !== 0 || gameMode === 'menu') return; if (isAIMode && currentPlayer !== 1) return; cellBg.alpha = 0.2; // Show cell is hoverable }; // Reset hover effect when mouse leaves self.up = function (x, y, obj) { if (self.value === 0) { cellBg.alpha = 0.01; // Reset to almost invisible } }; self.placeMarker = function (type, callback) { if (self.value !== 0) return false; // Prevent placing marker if already has one or is animating if (self.marker) return false; // In AI mode, extra validation if (isAIMode && type === 1 && aiProcessing) return false; if (isAIMode && type === 2 && !aiProcessing) 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, onFinish: callback }); } 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, onFinish: callback }); } LK.getSound('place').play(); return true; }; self.down = function (x, y, obj) { // Basic validation checks if (!gameReady || gameOver || gameMode === 'menu') return; if (self.value !== 0) return; // Cell already occupied // Prevent placing markers when game board is not visible if (gameBoard.alpha === 0) return; // In AI mode, prevent ALL player input during AI processing or when it's not player's turn if (isAIMode && (currentPlayer !== 1 || aiProcessing)) return; // Additional safety check to prevent race conditions if (isAIMode && currentPlayer === 2) return; // Extra validation: ensure no other animations are running if (isAIMode && aiProcessing) { console.log("Player input blocked - AI is processing"); return; } // Check if any cell is currently animating to prevent interference for (var r = 0; r < 3; r++) { for (var c = 0; c < 3; c++) { var checkCell = grid[r][c]; if (checkCell && checkCell.marker && checkCell.marker.alpha < 0.9 && checkCell.marker.alpha > 0) { return; // Animation in progress, prevent input } } } // Place the marker with animation and wait for completion if (self.placeMarker(currentPlayer, function () { // This callback runs after the marker animation completes checkWin(); if (!gameOver) { if (isAIMode && currentPlayer === 1) { // AI mode - player played, now AI plays currentPlayer = 2; updateTurnDisplay(); // Only start AI processing if not already processing if (!aiProcessing) { aiProcessing = true; LK.setTimeout(function () { if (!gameOver && currentPlayer === 2 && isAIMode) { aiProcessing = false; makeAIMove(); } else { aiProcessing = false; } }, 100); // Small delay to ensure UI updates } } 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(); } } })) { // Marker placement started successfully - disable further input until animation completes } }; 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', 'difficulty' var isAIMode = false; var aiDifficulty = 'normal'; // 'easy', 'normal', 'hard' var aiProcessing = false; // Flag to prevent AI from interfering with player moves var currentLanguage = storage.language || 'tr'; // 'tr' for Turkish, 'en' for English var selectedDifficulty = ''; // Store selected difficulty for confirmation var languageTexts = { tr: { blueTurn: 'Sıra: Mavinin', redTurn: 'Sıra: Kırmızının', blueWin: 'MAVİ TAKIM KAZANDI!', redWin: 'KIRMIZI TAKIM 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', back: '← Geri', exit: 'Çıkış', selectDifficulty: 'Zorluk Seçin', easy: 'Kolay', normal: 'Normal', hard: 'Zor', confirmEasy: 'Kolay modu seçmek istiyor musunuz?', confirmNormal: 'Normal modu seçmek istiyor musunuz?', confirmHard: 'Zor modu seçmek istiyor musunuz?', yes: 'Evet', no: 'Hayır' }, en: { blueTurn: 'Turn: Blue', redTurn: 'Turn: Red', blueWin: 'BLUE TEAM WON!', redWin: 'RED TEAM WON!', draw: 'DRAW!', playAgain: 'Play Again', language: 'English', vsAI: 'Play vs AI', twoPlayer: 'Two Player', selectMode: 'Select Game Mode', back: '← Back', exit: 'Exit', selectDifficulty: 'Select Difficulty', easy: 'Easy', normal: 'Normal', hard: 'Hard', confirmEasy: 'Do you want to select Easy mode?', confirmNormal: 'Do you want to select Normal mode?', confirmHard: 'Do you want to select Hard mode?', yes: 'Yes', no: 'No' } }; // 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; // Create background for play again button var playAgainBg = LK.getAsset('cell', { anchorX: 0.5, anchorY: 0.5, width: 500, height: 120, tint: 0x4CAF50, alpha: 0 }); LK.gui.center.addChild(playAgainBg); playAgainBg.y = 550; var playAgainBtn = new Text2(languageTexts[currentLanguage].playAgain, { size: 90, fill: 0xFFFFFF }); playAgainBtn.anchor.set(0.5, 0.5); playAgainBtn.interactive = false; playAgainBtn.hitArea = new Rectangle(-250, -60, 500, 120); 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; // AI Difficulty selection elements 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; // Create background blocks for difficulty buttons var easyBlock = LK.getAsset('cell', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 120, tint: 0x44FF44, alpha: 0.3 }); LK.gui.center.addChild(easyBlock); easyBlock.y = -100; easyBlock.alpha = 0; var normalBlock = LK.getAsset('cell', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 120, tint: 0xFFAA00, alpha: 0.3 }); LK.gui.center.addChild(normalBlock); normalBlock.y = 50; normalBlock.alpha = 0; var hardBlock = LK.getAsset('cell', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 120, tint: 0xFF4444, alpha: 0.3 }); LK.gui.center.addChild(hardBlock); hardBlock.y = 200; hardBlock.alpha = 0; var easyBtn = new Text2(languageTexts[currentLanguage].easy, { size: 90, fill: 0x44FF44 }); easyBtn.anchor.set(0.5, 0.5); easyBtn.interactive = false; easyBtn.hitArea = new Rectangle(-150, -45, 300, 90); LK.gui.center.addChild(easyBtn); easyBtn.y = -100; easyBtn.alpha = 0; var normalBtn = new Text2(languageTexts[currentLanguage].normal, { size: 90, fill: 0xFFAA00 }); normalBtn.anchor.set(0.5, 0.5); normalBtn.interactive = false; normalBtn.hitArea = new Rectangle(-150, -45, 300, 90); LK.gui.center.addChild(normalBtn); normalBtn.y = 50; normalBtn.alpha = 0; var hardBtn = new Text2(languageTexts[currentLanguage].hard, { size: 90, fill: 0xFF4444 }); hardBtn.anchor.set(0.5, 0.5); hardBtn.interactive = false; hardBtn.hitArea = new Rectangle(-150, -45, 300, 90); LK.gui.center.addChild(hardBtn); hardBtn.y = 200; hardBtn.alpha = 0; // Confirmation dialog elements var confirmationBg = LK.getAsset('gridBackground', { anchorX: 0.5, anchorY: 0.5, width: 1200, height: 600, tint: 0x000000, alpha: 0.8 }); LK.gui.center.addChild(confirmationBg); confirmationBg.alpha = 0; var confirmationText = new Text2('', { size: 80, fill: 0xFFFFFF }); confirmationText.anchor.set(0.5, 0.5); LK.gui.center.addChild(confirmationText); confirmationText.y = -50; confirmationText.alpha = 0; var yesBtn = new Text2(languageTexts[currentLanguage].yes, { size: 80, fill: 0x44FF44 }); yesBtn.anchor.set(0.5, 0.5); yesBtn.interactive = true; LK.gui.center.addChild(yesBtn); yesBtn.x = -150; yesBtn.y = 100; yesBtn.alpha = 0; var noBtn = new Text2(languageTexts[currentLanguage].no, { size: 80, fill: 0xFF4444 }); noBtn.anchor.set(0.5, 0.5); noBtn.interactive = true; LK.gui.center.addChild(noBtn); noBtn.x = 150; noBtn.y = 100; noBtn.alpha = 0; // Add hit areas to confirmation dialog buttons yesBtn.hitArea = new Rectangle(-100, -40, 200, 80); noBtn.hitArea = new Rectangle(-100, -40, 200, 80); // Confirmation dialog button handlers yesBtn.down = function (x, y, obj) { if (selectedDifficulty) { aiDifficulty = selectedDifficulty; hideConfirmationDialog(); startGame('ai'); } }; noBtn.down = function (x, y, obj) { hideConfirmationDialog(); }; // Add glowing animation to blocks function animateDifficultyBlocks() { tween(easyBlock, { alpha: 0.5 }, { duration: 1000, easing: tween.easeInOut, repeat: -1, yoyo: true }); tween(normalBlock, { alpha: 0.5 }, { duration: 1000, easing: tween.easeInOut, repeat: -1, yoyo: true, delay: 333 }); tween(hardBlock, { alpha: 0.5 }, { duration: 1000, easing: tween.easeInOut, repeat: -1, yoyo: true, delay: 666 }); } function showConfirmationDialog(difficulty) { selectedDifficulty = difficulty; gameMode = 'confirmation'; var confirmText = ''; if (difficulty === 'easy') { confirmText = languageTexts[currentLanguage].confirmEasy; } else if (difficulty === 'normal') { confirmText = languageTexts[currentLanguage].confirmNormal; } else if (difficulty === 'hard') { confirmText = languageTexts[currentLanguage].confirmHard; } confirmationText.setText(confirmText); // Enable confirmation button interactions yesBtn.interactive = true; noBtn.interactive = true; // Show confirmation elements tween(confirmationBg, { alpha: 0.8 }, { duration: 200 }); tween(confirmationText, { alpha: 1 }, { duration: 200 }); tween(yesBtn, { alpha: 1 }, { duration: 200 }); tween(noBtn, { alpha: 1 }, { duration: 200 }); } function hideConfirmationDialog() { gameMode = 'difficulty'; selectedDifficulty = ''; // Disable confirmation button interactions yesBtn.interactive = false; noBtn.interactive = false; // Hide confirmation elements tween(confirmationBg, { alpha: 0 }, { duration: 200 }); tween(confirmationText, { alpha: 0 }, { duration: 200 }); tween(yesBtn, { alpha: 0 }, { duration: 200 }); tween(noBtn, { alpha: 0 }, { duration: 200 }); } var vsAIBtn = new Text2(languageTexts[currentLanguage].vsAI, { size: 100, fill: 0x00AAFF }); vsAIBtn.anchor.set(0.5, 0.5); vsAIBtn.interactive = false; vsAIBtn.hitArea = new Rectangle(-200, -50, 400, 100); 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); twoPlayerBtn.interactive = false; twoPlayerBtn.hitArea = new Rectangle(-200, -50, 400, 100); 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); backBtn.interactive = false; backBtn.hitArea = new Rectangle(-150, -40, 300, 80); LK.gui.top.addChild(backBtn); backBtn.x = 250; backBtn.y = 160; backBtn.alpha = 0; var exitBtn = new Text2(languageTexts[currentLanguage].exit, { size: 80, fill: 0xFF0000 }); exitBtn.anchor.set(0.5, 0); exitBtn.interactive = false; exitBtn.hitArea = new Rectangle(-100, -40, 200, 80); LK.gui.top.addChild(exitBtn); exitBtn.y = 80; exitBtn.x = 450; exitBtn.alpha = 0; // Language toggle functionality languageBtn.interactive = true; // Always interactive languageBtn.hitArea = new Rectangle(-100, -40, 200, 80); 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(); } }; twoPlayerBtn.down = function (x, y, obj) { if (gameMode === 'menu') { startGame('twoPlayer'); } }; // Difficulty button handlers easyBtn.down = function (x, y, obj) { if (gameMode === 'difficulty') { showConfirmationDialog('easy'); } }; normalBtn.down = function (x, y, obj) { if (gameMode === 'difficulty') { showConfirmationDialog('normal'); } }; hardBtn.down = function (x, y, obj) { if (gameMode === 'difficulty') { showConfirmationDialog('hard'); } }; backBtn.down = function (x, y, obj) { if (gameMode === 'confirmation') { hideConfirmationDialog(); } else 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); normalBtn.setText(languageTexts[currentLanguage].normal); hardBtn.setText(languageTexts[currentLanguage].hard); yesBtn.setText(languageTexts[currentLanguage].yes); noBtn.setText(languageTexts[currentLanguage].no); // Update winner/draw text if game is over if (gameOver && statusText.alpha > 0) { var currentText = statusText.text; // Check if current text matches any winner or draw text from either language var isBlueWin = currentText === languageTexts.tr.blueWin || currentText === languageTexts.en.blueWin; var isRedWin = currentText === languageTexts.tr.redWin || currentText === languageTexts.en.redWin; var isDraw = currentText === languageTexts.tr.draw || currentText === languageTexts.en.draw; if (isBlueWin) { statusText.setText(languageTexts[currentLanguage].blueWin); } else if (isRedWin) { statusText.setText(languageTexts[currentLanguage].redWin); } else if (isDraw) { statusText.setText(languageTexts[currentLanguage].draw); } } } // 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; // Enable main menu interactions vsAIBtn.interactive = true; twoPlayerBtn.interactive = true; blueTurnText.alpha = 0; redTurnText.alpha = 0; statusText.alpha = 0; playAgainBtn.alpha = 0; playAgainBtn.interactive = false; playAgainBg.alpha = 0; backBtn.alpha = 0; backBtn.interactive = false; exitBtn.alpha = 0; exitBtn.interactive = false; // Hide difficulty selection difficultyTitle.alpha = 0; easyBtn.alpha = 0; normalBtn.alpha = 0; hardBtn.alpha = 0; // Disable difficulty button interactions easyBtn.interactive = false; normalBtn.interactive = false; hardBtn.interactive = false; // Hide blocks // Stop any active animations on difficulty blocks tween.stop(easyBlock); tween.stop(normalBlock); tween.stop(hardBlock); easyBlock.alpha = 0; normalBlock.alpha = 0; hardBlock.alpha = 0; // Hide confirmation dialog confirmationBg.alpha = 0; confirmationText.alpha = 0; yesBtn.alpha = 0; noBtn.alpha = 0; // Disable confirmation dialog interactions yesBtn.interactive = false; noBtn.interactive = false; } function showDifficultySelection() { gameMode = 'difficulty'; // Hide main menu menuTitle.alpha = 0; vsAIBtn.alpha = 0; twoPlayerBtn.alpha = 0; // Disable main menu interactions vsAIBtn.interactive = false; twoPlayerBtn.interactive = false; // Show difficulty selection difficultyTitle.alpha = 1; easyBtn.alpha = 1; normalBtn.alpha = 1; hardBtn.alpha = 1; backBtn.alpha = 1; // Enable difficulty button interactions easyBtn.interactive = true; normalBtn.interactive = true; hardBtn.interactive = true; backBtn.interactive = true; // Show and animate blocks easyBlock.alpha = 0.3; normalBlock.alpha = 0.3; hardBlock.alpha = 0.3; animateDifficultyBlocks(); } function startGame(mode) { gameMode = mode; isAIMode = mode === 'ai'; gameBoard.alpha = 1; menuTitle.alpha = 0; vsAIBtn.alpha = 0; twoPlayerBtn.alpha = 0; // Disable menu interactions vsAIBtn.interactive = false; twoPlayerBtn.interactive = false; // Hide difficulty selection difficultyTitle.alpha = 0; easyBtn.alpha = 0; normalBtn.alpha = 0; hardBtn.alpha = 0; // Disable difficulty button interactions easyBtn.interactive = false; normalBtn.interactive = false; hardBtn.interactive = false; // Hide blocks easyBlock.alpha = 0; normalBlock.alpha = 0; hardBlock.alpha = 0; // Hide confirmation dialog confirmationBg.alpha = 0; confirmationText.alpha = 0; yesBtn.alpha = 0; noBtn.alpha = 0; // Disable confirmation dialog interactions yesBtn.interactive = false; noBtn.interactive = false; // Hide back button in all game modes once game starts backBtn.alpha = 0; backBtn.interactive = false; exitBtn.alpha = 1; // Enable exit button interaction exitBtn.interactive = true; // In AI mode, player (1) always starts first if (isAIMode) { currentPlayer = 1; } else { // 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 with background playAgainBtn.interactive = true; tween(playAgainBg, { alpha: 0.9 }, { duration: 500 }); tween(playAgainBtn, { alpha: 1 }, { duration: 500 }); // Add pulsing animation to make it more prominent tween(playAgainBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeInOut, repeat: -1, yoyo: true }); } 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 with background playAgainBtn.interactive = true; tween(playAgainBg, { alpha: 0.9 }, { duration: 500 }); tween(playAgainBtn, { alpha: 1 }, { duration: 500 }); // Add pulsing animation to make it more prominent tween(playAgainBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeInOut, repeat: -1, yoyo: true }); } function makeAIMove() { if (gameOver || currentPlayer !== 2) return; // Extra validation for game state if (!isAIMode || gameMode !== 'ai') return; // Don't proceed if AI is already processing if (aiProcessing) return; // Set AI processing flag to prevent consecutive moves aiProcessing = true; // Add safety timeout to clear flag in case of errors var safetyTimeout = LK.setTimeout(function () { if (aiProcessing && !gameOver) { console.log("AI safety timeout triggered - clearing processing flag"); aiProcessing = false; // Safety clear after 5 seconds } }, 5000); // Variable thinking time based on difficulty var minTime, maxTime; if (aiDifficulty === 'easy') { minTime = 300; maxTime = 800; } else if (aiDifficulty === 'normal') { minTime = 500; maxTime = 1200; } else { // hard minTime = 800; maxTime = 2000; } var thinkingTime = minTime + Math.floor(Math.random() * (maxTime - minTime)); // Use setTimeout to yield frame and prevent blocking LK.setTimeout(function () { // Clear the safety timeout since we're now processing if (safetyTimeout) { LK.clearTimeout(safetyTimeout); safetyTimeout = null; } // Recheck game state after timeout - ensure AI flag is always cleared if (gameOver || currentPlayer !== 2 || !isAIMode || gameMode !== 'ai') { aiProcessing = false; return; } // Additional validation to prevent AI moves during player turn if (isAIMode && currentPlayer === 1) { aiProcessing = false; return; } // Extra safety check - if game state is invalid, clear flag if (!isAIMode || gameMode !== 'ai') { aiProcessing = false; return; } // Find best move using difficulty-specific strategy var bestMove = findBestMove(); var moveSuccess = false; // Validate that we have a valid move and it's still empty if (bestMove && bestMove.value === 0) { // Validate coordinates are within bounds if (bestMove.row >= 0 && bestMove.row < 3 && bestMove.col >= 0 && bestMove.col < 3) { // Double check the cell is still valid before AI places marker var targetCell = grid[bestMove.row][bestMove.col]; if (targetCell && targetCell.value === 0 && !gameOver && currentPlayer === 2 && !targetCell.marker) { if (targetCell.placeMarker(2, function () { // This callback runs after the AI marker animation completes checkWin(); if (!gameOver) { currentPlayer = 1; updateTurnDisplay(); } // Clear AI processing flag after move is complete aiProcessing = false; })) { moveSuccess = true; } } } } // If the best move failed for any reason, find any empty cell as fallback if (!moveSuccess && !gameOver && currentPlayer === 2) { var emptyCells = []; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (grid[row][col].value === 0 && !grid[row][col].marker) { emptyCells.push(grid[row][col]); } } } // Try to place marker on any empty cell if (emptyCells.length > 0) { var fallbackCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; if (fallbackCell.placeMarker(2, function () { // This callback runs after the fallback AI marker animation completes checkWin(); if (!gameOver) { currentPlayer = 1; updateTurnDisplay(); } // Clear AI processing flag after move is complete aiProcessing = false; })) { moveSuccess = true; } } } // If no move was made, clear the processing flag if (!moveSuccess) { aiProcessing = false; } }, thinkingTime); } function findBestMove() { var emptyCells = []; var winningMoves = []; var blockingMoves = []; var forkMoves = []; var blockForkMoves = []; var cornerCells = []; var edgeCells = []; var validCells = []; // Collect all valid empty cells for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { var cell = grid[row][col]; if (cell && cell.value === 0) { emptyCells.push(cell); validCells.push(cell); // Categorize cell positions if ((row === 0 || row === 2) && (col === 0 || col === 2)) { cornerCells.push(cell); } else if (row === 1 && col === 1) { // Center cell - handled separately } else { edgeCells.push(cell); } } } } // Early exit if no valid moves if (validCells.length === 0) { return null; } // Ensure we always have at least one valid cell to return if (emptyCells.length === 0) { return null; } // Easy mode: Make deliberate mistakes and play suboptimally if (aiDifficulty === 'easy') { // 70% chance to make a completely random move if (Math.random() < 0.7) { return validCells[Math.floor(Math.random() * validCells.length)]; } // 20% chance to avoid winning moves (make mistakes) if (Math.random() < 0.2) { var nonWinningMoves = []; for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 2; if (!checkWinCondition(2)) { nonWinningMoves.push(cell); } cell.value = 0; } if (nonWinningMoves.length > 0) { return nonWinningMoves[Math.floor(Math.random() * nonWinningMoves.length)]; } } // 10% chance to miss blocking player wins if (Math.random() < 0.1) { var nonBlockingMoves = []; for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 1; if (!checkWinCondition(1)) { nonBlockingMoves.push(cell); } cell.value = 0; } if (nonBlockingMoves.length > 0) { return nonBlockingMoves[Math.floor(Math.random() * nonBlockingMoves.length)]; } } // Default: play randomly from all valid moves return validCells[Math.floor(Math.random() * validCells.length)]; } // Normal mode: Balanced play with occasional mistakes if (aiDifficulty === 'normal') { // Priority 1: Check if AI can win immediately (100% success rate for winning moves) for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 2; if (checkWinCondition(2)) { cell.value = 0; // Always take winning move in normal mode return cell; } cell.value = 0; } // Priority 2: Block player from winning (98% success rate) for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 1; if (checkWinCondition(1)) { cell.value = 0; // Block with 98% probability (2% miss chance for difficulty) if (Math.random() > 0.02) { return cell; } } cell.value = 0; } // Priority 3: Create forks (85% success rate) if (Math.random() < 0.85) { forkMoves = checkForFork(2); if (forkMoves.length > 0) { return forkMoves[0]; } } // Priority 4: Block player forks (80% success rate) if (Math.random() < 0.8) { var playerForkMoves = checkForFork(1); if (playerForkMoves.length > 0) { // When player can create a fork, we need to be smart about blocking // If there's only one fork move, block it if (playerForkMoves.length === 1) { return playerForkMoves[0]; } else { // Multiple fork possibilities - create a threat to force player to defend // Look for a move that creates a winning threat for us for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 2; // Check if this creates a winning threat var createsThreats = false; for (var j = 0; j < emptyCells.length; j++) { if (i !== j) { var testCell = emptyCells[j]; testCell.value = 2; if (checkWinCondition(2)) { createsThreats = true; } testCell.value = 0; } } cell.value = 0; // If this move creates a threat and blocks at least one fork path if (createsThreats) { return cell; } } // If no good counter-threat, block one of the fork moves return playerForkMoves[0]; } } } // Priority 5: Basic strategy - prefer center and corners (70% of the time) if (Math.random() < 0.7) { var strategicMoves = []; // Center is most valuable if (grid[1][1].value === 0) { strategicMoves.push(grid[1][1]); strategicMoves.push(grid[1][1]); // Double weight for center } // Corners are second most valuable for (var i = 0; i < cornerCells.length; i++) { strategicMoves.push(cornerCells[i]); } if (strategicMoves.length > 0) { return strategicMoves[Math.floor(Math.random() * strategicMoves.length)]; } } // Priority 6: If no strategic moves, ensure we always return a valid move if (emptyCells.length > 0) { return emptyCells[Math.floor(Math.random() * emptyCells.length)]; } } // Advanced tactic evaluation functions function countWinningLines(row, col, player) { var count = 0; // Check row var rowCount = 0; for (var c = 0; c < 3; c++) { if (grid[row][c].value === player || c === col) { rowCount++; } else if (grid[row][c].value !== 0) { rowCount = -10; // Blocked break; } } if (rowCount === 3) count++; // Check column var colCount = 0; for (var r = 0; r < 3; r++) { if (grid[r][col].value === player || r === row) { colCount++; } else if (grid[r][col].value !== 0) { colCount = -10; // Blocked break; } } if (colCount === 3) count++; // Check diagonals if (row === 0 && col === 0 || row === 1 && col === 1 || row === 2 && col === 2) { var diagCount = 0; for (var i = 0; i < 3; i++) { if (grid[i][i].value === player || i === row && i === col) { diagCount++; } else if (grid[i][i].value !== 0) { diagCount = -10; // Blocked break; } } if (diagCount === 3) count++; } if (row === 0 && col === 2 || row === 1 && col === 1 || row === 2 && col === 0) { var antiDiagCount = 0; for (var i = 0; i < 3; i++) { if (grid[i][2 - i].value === player || i === row && 2 - i === col) { antiDiagCount++; } else if (grid[i][2 - i].value !== 0) { antiDiagCount = -10; // Blocked break; } } if (antiDiagCount === 3) count++; } return count; } // Evaluate move position strength function evaluatePosition(cell) { var score = 0; var row = cell.row; var col = cell.col; // Center is most valuable if (row === 1 && col === 1) score += 4; // Corners are second most valuable else if ((row === 0 || row === 2) && (col === 0 || col === 2)) score += 3; // Edges are least valuable else score += 1; // Check potential winning lines var potentialLines = countWinningLines(row, col, 2); score += potentialLines * 2; return score; } // Advanced position evaluation for hard mode AI function evaluateHardModePosition(cell) { var score = 0; var row = cell.row; var col = cell.col; // Base positional values with strategic weighting if (row === 1 && col === 1) { score += 15; // Center dominance } else if ((row === 0 || row === 2) && (col === 0 || col === 2)) { score += 12; // Corner control } else { score += 8; // Edge positions } // Evaluate line potential (how many lines this position affects) var linesControlled = 0; var lineValues = 0; // Check row control var rowEmpty = 0, rowAI = 0, rowPlayer = 0; for (var c = 0; c < 3; c++) { if (grid[row][c].value === 0) rowEmpty++;else if (grid[row][c].value === 2) rowAI++;else rowPlayer++; } if (rowPlayer === 0) { linesControlled++; lineValues += rowAI * 3 + rowEmpty; } // Check column control var colEmpty = 0, colAI = 0, colPlayer = 0; for (var r = 0; r < 3; r++) { if (grid[r][col].value === 0) colEmpty++;else if (grid[r][col].value === 2) colAI++;else colPlayer++; } if (colPlayer === 0) { linesControlled++; lineValues += colAI * 3 + colEmpty; } // Check main diagonal if (row === col) { var diagEmpty = 0, diagAI = 0, diagPlayer = 0; for (var i = 0; i < 3; i++) { if (grid[i][i].value === 0) diagEmpty++;else if (grid[i][i].value === 2) diagAI++;else diagPlayer++; } if (diagPlayer === 0) { linesControlled++; lineValues += diagAI * 3 + diagEmpty; } } // Check anti-diagonal if (row + col === 2) { var antiDiagEmpty = 0, antiDiagAI = 0, antiDiagPlayer = 0; for (var i = 0; i < 3; i++) { if (grid[i][2 - i].value === 0) antiDiagEmpty++;else if (grid[i][2 - i].value === 2) antiDiagAI++;else antiDiagPlayer++; } if (antiDiagPlayer === 0) { linesControlled++; lineValues += antiDiagAI * 3 + antiDiagEmpty; } } score += linesControlled * 6 + lineValues * 2; // Strategic pattern bonuses // Opposite corner control if (row === 0 && col === 0 && grid[2][2].value === 2 || row === 2 && col === 2 && grid[0][0].value === 2 || row === 0 && col === 2 && grid[2][0].value === 2 || row === 2 && col === 0 && grid[0][2].value === 2) { score += 8; // Diagonal dominance } // Adjacent to AI pieces (connection bonus) var adjacentAI = 0; var checkAdj = [{ r: row - 1, c: col }, { r: row + 1, c: col }, { r: row, c: col - 1 }, { r: row, c: col + 1 }]; for (var i = 0; i < checkAdj.length; i++) { var adj = checkAdj[i]; if (adj.r >= 0 && adj.r < 3 && adj.c >= 0 && adj.c < 3) { if (grid[adj.r][adj.c].value === 2) adjacentAI++; } } score += adjacentAI * 4; return score; } // Check for forks (moves that create two winning threats) function checkForFork(player) { var forkCells = []; for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = player; var winningLineCount = 0; // Count how many winning moves this creates for (var j = 0; j < emptyCells.length; j++) { if (i !== j) { var testCell = emptyCells[j]; testCell.value = player; if (checkWinCondition(player)) { winningLineCount++; } testCell.value = 0; } } cell.value = 0; if (winningLineCount >= 2) { forkCells.push(cell); } } return forkCells; } // Adjust strategy based on difficulty var aiPersonality = Math.floor(Math.random() * 8); var randomnessFactor = Math.random(); // Difficulty-based mistake chances var winMissChance, blockMissChance, strategyQuality; if (aiDifficulty === 'easy') { // Easy mode doesn't need these values as it plays randomly winMissChance = 1.0; // Always miss winning moves (not that it matters) blockMissChance = 1.0; // Always miss blocking (not that it matters) strategyQuality = 0; // No strategic moves aiPersonality = -1; // No personality, just random } else if (aiDifficulty === 'normal') { winMissChance = 0.02; // 2% chance to miss winning move blockMissChance = 0.05; // 5% chance to miss blocking strategyQuality = 0.6; // Medium quality strategic moves } else { // hard - Perfect play with zero mistakes winMissChance = 0; // NEVER miss winning move blockMissChance = 0; // NEVER miss blocking moves strategyQuality = 1.0; // Perfect strategic moves aiPersonality = Math.floor(Math.random() * 8); // Use all personalities for variety } // Hard mode AI - Perfect play with advanced strategy evaluation if (aiDifficulty === 'hard') { // Priority 1: ALWAYS win if possible (0% miss rate) for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 2; if (checkWinCondition(2)) { cell.value = 0; return cell; // IMMEDIATELY take winning move - no randomness } cell.value = 0; } // Priority 2: ALWAYS block player wins (0% miss rate) for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 1; if (checkWinCondition(1)) { cell.value = 0; return cell; // IMMEDIATELY block - no randomness } cell.value = 0; } // Priority 3: Create forks (multiple winning threats) forkMoves = checkForFork(2); if (forkMoves.length > 0) { return forkMoves[0]; // Take any fork opportunity } // Priority 4: Block player forks with counter-strategy var playerForkMoves = checkForFork(1); if (playerForkMoves.length > 0) { // If player can create multiple forks, create a counter-threat if (playerForkMoves.length > 1) { // Look for moves that force player to defend while blocking fork for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; cell.value = 2; var threatsCreated = 0; for (var j = 0; j < emptyCells.length; j++) { if (i !== j) { var testCell = emptyCells[j]; testCell.value = 2; if (checkWinCondition(2)) { threatsCreated++; } testCell.value = 0; } } cell.value = 0; if (threatsCreated > 0) { return cell; // Counter-threat to force defense } } } // Otherwise block the fork return playerForkMoves[0]; } // Priority 5: Optimal opening strategy if (emptyCells.length === 9) { // First move: always take center or corner if (grid[1][1].value === 0) { return grid[1][1]; // Center is strongest opening } else { return cornerCells[0]; // Fallback to corner } } if (emptyCells.length === 8) { // Second move response if (grid[1][1].value === 1) { // Player took center, take any corner return cornerCells[0]; } else { // Player took corner or edge, take center if (grid[1][1].value === 0) { return grid[1][1]; } } } // Priority 6: Advanced positional strategy with minimax evaluation var bestMove = null; var bestScore = -1000; for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; var score = evaluateHardModePosition(cell); // Add advanced tactical bonuses if (cell.row === 1 && cell.col === 1) score += 15; // Center control if ((cell.row === 0 || cell.row === 2) && (cell.col === 0 || cell.col === 2)) score += 12; // Corner control // Evaluate future position strength cell.value = 2; var futureThreats = 0; for (var j = 0; j < emptyCells.length; j++) { if (i !== j) { var testCell = emptyCells[j]; testCell.value = 2; if (checkWinCondition(2)) futureThreats++; testCell.value = 0; } } score += futureThreats * 8; // Bonus for creating future threats cell.value = 0; if (score > bestScore) { bestScore = score; bestMove = cell; } } if (bestMove) { return bestMove; } } // Priority 3: Check for fork opportunities (based on difficulty) if (aiDifficulty !== 'easy' && randomnessFactor < strategyQuality) { forkMoves = checkForFork(2); if (forkMoves.length > 0 && randomnessFactor > 1 - strategyQuality) { return forkMoves[Math.floor(Math.random() * forkMoves.length)]; } } // Priority 4: Block player forks (based on difficulty) if (aiDifficulty !== 'easy' && randomnessFactor < strategyQuality) { blockForkMoves = checkForFork(1); if (blockForkMoves.length > 0 && randomnessFactor > 1 - strategyQuality) { // Force player to defend instead of creating fork return blockForkMoves[Math.floor(Math.random() * blockForkMoves.length)]; } } // Priority 5: Advanced tactical personalities var moveChoices = []; var moveScores = []; if (aiPersonality === 0) { // Perfect player - uses optimal strategy if (emptyCells.length === 9) { // First move: prefer corner moveChoices = cornerCells.slice(); } else if (emptyCells.length === 8) { // Second move response if (grid[1][1].value === 1) { // Player took center, take corner moveChoices = cornerCells.slice(); } else { // Player took corner or edge, take center if (grid[1][1].value === 0) { moveChoices.push(grid[1][1]); } } } else { // Evaluate all positions for (var i = 0; i < emptyCells.length; i++) { var score = evaluatePosition(emptyCells[i]); moveScores.push({ cell: emptyCells[i], score: score }); } moveScores.sort(function (a, b) { return b.score - a.score; }); // Take top scoring moves var topScore = moveScores[0].score; for (var i = 0; i < moveScores.length; i++) { if (moveScores[i].score >= topScore - 1) { moveChoices.push(moveScores[i].cell); } } } } else if (aiPersonality === 1) { // Trap setter - tries to create winning patterns if (grid[1][1].value === 0) { moveChoices.push(grid[1][1]); moveChoices.push(grid[1][1]); // Double weight } // Look for L-shaped patterns if (grid[0][0].value === 2 && grid[2][2].value === 0) { moveChoices.push(grid[2][2]); } if (grid[0][2].value === 2 && grid[2][0].value === 0) { moveChoices.push(grid[2][0]); } // Add corners for trap potential if (cornerCells.length > 0) { moveChoices = moveChoices.concat(cornerCells); } } else if (aiPersonality === 2) { // Defensive player - blocks and controls center if (grid[1][1].value === 0) { moveChoices.push(grid[1][1]); moveChoices.push(grid[1][1]); moveChoices.push(grid[1][1]); // Triple weight for center } // Prefer moves that block multiple lines for (var i = 0; i < emptyCells.length; i++) { var blockCount = countWinningLines(emptyCells[i].row, emptyCells[i].col, 1); if (blockCount >= 2) { moveChoices.push(emptyCells[i]); } } if (moveChoices.length === 0 && cornerCells.length > 0) { moveChoices = cornerCells.slice(); } } else if (aiPersonality === 3) { // Mirror player - plays symmetrically var playerMoves = []; for (var r = 0; r < 3; r++) { for (var c = 0; c < 3; c++) { if (grid[r][c].value === 1) { playerMoves.push({ row: r, col: c }); } } } if (playerMoves.length > 0) { var lastMove = playerMoves[playerMoves.length - 1]; // Try to mirror across center var mirrorRow = 2 - lastMove.row; var mirrorCol = 2 - lastMove.col; if (grid[mirrorRow][mirrorCol].value === 0) { moveChoices.push(grid[mirrorRow][mirrorCol]); moveChoices.push(grid[mirrorRow][mirrorCol]); // Double weight } } // Add strategic positions if (grid[1][1].value === 0) { moveChoices.push(grid[1][1]); } if (cornerCells.length > 0) { moveChoices.push(cornerCells[Math.floor(Math.random() * cornerCells.length)]); } } else if (aiPersonality === 4) { // Edge control player - unusual but effective if (edgeCells.length > 0) { // Prefer edges that connect to existing pieces for (var i = 0; i < edgeCells.length; i++) { var edge = edgeCells[i]; var adjacentAI = 0; // Check adjacent cells var checkPositions = [{ r: edge.row - 1, c: edge.col }, { r: edge.row + 1, c: edge.col }, { r: edge.row, c: edge.col - 1 }, { r: edge.row, c: edge.col + 1 }]; for (var j = 0; j < checkPositions.length; j++) { var pos = checkPositions[j]; if (pos.r >= 0 && pos.r < 3 && pos.c >= 0 && pos.c < 3) { if (grid[pos.r][pos.c].value === 2) { adjacentAI++; } } } if (adjacentAI > 0) { moveChoices.push(edge); moveChoices.push(edge); // Double weight for connected edges } } if (moveChoices.length === 0) { moveChoices = edgeCells.slice(); } } // Sometimes take center if (Math.random() < 0.3 && grid[1][1].value === 0) { moveChoices.push(grid[1][1]); } } else if (aiPersonality === 5) { // Chaos player - creates complex board states var complexityScore = []; for (var i = 0; i < emptyCells.length; i++) { var cell = emptyCells[i]; // Calculate how many lines this move affects var linesAffected = 0; if (true) linesAffected++; // Row if (true) linesAffected++; // Column if (cell.row === cell.col || cell.row + cell.col === 2) { linesAffected++; // Diagonal } complexityScore.push({ cell: cell, complexity: linesAffected }); } // Prefer high complexity moves complexityScore.sort(function (a, b) { return b.complexity - a.complexity; }); for (var i = 0; i < Math.min(3, complexityScore.length); i++) { moveChoices.push(complexityScore[i].cell); } } else if (aiPersonality === 6) { // Pattern player - follows specific winning patterns var patterns = [ // Corner-opposite-corner [{ r: 0, c: 0 }, { r: 2, c: 2 }], [{ r: 0, c: 2 }, { r: 2, c: 0 }], // L-shapes [{ r: 0, c: 0 }, { r: 0, c: 2 }], [{ r: 0, c: 0 }, { r: 2, c: 0 }], [{ r: 2, c: 2 }, { r: 2, c: 0 }], [{ r: 2, c: 2 }, { r: 0, c: 2 }]]; for (var p = 0; p < patterns.length; p++) { var pattern = patterns[p]; var hasFirst = grid[pattern[0].r][pattern[0].c].value === 2; var secondEmpty = grid[pattern[1].r][pattern[1].c].value === 0; if (hasFirst && secondEmpty) { moveChoices.push(grid[pattern[1].r][pattern[1].c]); moveChoices.push(grid[pattern[1].r][pattern[1].c]); // Double weight } } // Default to smart positions if (moveChoices.length === 0) { if (grid[1][1].value === 0) { moveChoices.push(grid[1][1]); } if (cornerCells.length > 0) { moveChoices = moveChoices.concat(cornerCells); } } } else { // Adaptive player - changes strategy based on game state var gamePhase = 9 - emptyCells.length; // 0-8, higher = later if (gamePhase <= 2) { // Early game: control key positions if (grid[1][1].value === 0) { moveChoices.push(grid[1][1]); moveChoices.push(grid[1][1]); } if (cornerCells.length > 0) { moveChoices = moveChoices.concat(cornerCells); } } else if (gamePhase <= 5) { // Mid game: create threats for (var i = 0; i < emptyCells.length; i++) { var threatLevel = countWinningLines(emptyCells[i].row, emptyCells[i].col, 2); if (threatLevel >= 1) { moveChoices.push(emptyCells[i]); if (threatLevel >= 2) { moveChoices.push(emptyCells[i]); // Extra weight for multi-threats } } } } else { // Late game: optimize winning chances for (var i = 0; i < emptyCells.length; i++) { moveChoices.push(emptyCells[i]); } } } // Add calculated randomness if (Math.random() < 0.1) { // 10% chance to add unexpected move if (validCells.length > 0) { var randomCell = validCells[Math.floor(Math.random() * validCells.length)]; moveChoices.push(randomCell); } } // If we have strategic choices, pick from them if (moveChoices.length > 0) { // Sometimes pick the most frequent choice (if duplicates exist) var choiceFrequency = {}; for (var i = 0; i < moveChoices.length; i++) { var key = moveChoices[i].row + ',' + moveChoices[i].col; choiceFrequency[key] = (choiceFrequency[key] || 0) + 1; } // Create weighted selection var weightedChoices = []; for (var i = 0; i < moveChoices.length; i++) { weightedChoices.push(moveChoices[i]); } return weightedChoices[Math.floor(Math.random() * weightedChoices.length)]; } // Easy mode already handled above with helpful logic // Final fallback: intelligent selection var fallbackChoices = []; // Prioritize based on position value (less strategic in easy mode) if (aiDifficulty !== 'easy' || Math.random() < 0.5) { if (grid[1][1].value === 0) { fallbackChoices.push(grid[1][1]); } } if (cornerCells.length > 0 && (aiDifficulty !== 'easy' || Math.random() < 0.6)) { fallbackChoices = fallbackChoices.concat(cornerCells); } if (edgeCells.length > 0 && (aiDifficulty === 'easy' || Math.random() < 0.3)) { fallbackChoices.push(edgeCells[Math.floor(Math.random() * edgeCells.length)]); } if (fallbackChoices.length === 0) { fallbackChoices = validCells; } if (fallbackChoices.length > 0) { return fallbackChoices[Math.floor(Math.random() * fallbackChoices.length)]; } // Ultimate fallback: return first empty cell found for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (grid[row][col].value === 0 && !grid[row][col].marker) { return grid[row][col]; } } } // If still no valid move found, return any empty cell regardless of marker status for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { if (grid[row][col].value === 0) { return grid[row][col]; } } } 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; // Immediately clear AI processing to prevent stuck states aiProcessing = false; // Clear any pending AI timeouts LK.clearTimeout(); // Clear any pending timeouts // Stop all active tweens to prevent conflicts tween.stop(); // Force clear any stuck AI state if (isAIMode && currentPlayer === 2) { currentPlayer = 1; // Reset to player's turn } // 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) { // Stop any active tweens on marker before destroying tween.stop(cell.marker); // Reset marker properties to prevent visual glitches cell.marker.alpha = 0; cell.marker.scaleX = 1; cell.marker.scaleY = 1; cell.marker.tint = 0xffffff; cell.marker.rotation = 0; // Remove from parent before destroying if (cell.marker.parent) { cell.marker.parent.removeChild(cell.marker); } cell.marker.destroy(); cell.marker = null; } } } // Clear UI statusText.setText(''); statusText.alpha = 0; playAgainBtn.alpha = 0; playAgainBg.alpha = 0; // Stop any pulsing animation on play again button tween.stop(playAgainBg); tween.stop(playAgainBtn); playAgainBg.scaleX = 1; playAgainBg.scaleY = 1; playAgainBtn.interactive = false; // Remove win line if (winLine) { // Stop any active tweens on win line to prevent memory leaks tween.stop(winLine.line); tween.stop(winLine); // Stop any tweens on the container too // Reset line properties before destroying winLine.line.alpha = 0; winLine.line.scaleX = 0; winLine.line.tint = 0xffffff; // Reset tint // Remove from parent before destroying if (winLine.parent) { winLine.parent.removeChild(winLine); } winLine.destroy(); winLine = null; } updateTurnDisplay(); } // Play again button handler playAgainBtn.down = function (x, y, obj) { if (gameOver) { resetGame(); } }; // Add small delay to prevent initial freeze when entering game var gameReady = false; LK.setTimeout(function () { gameReady = true; }, 50); // Reduced delay for better responsiveness
===================================================================
--- original.js
+++ change.js
@@ -805,16 +805,11 @@
noBtn.alpha = 0;
// Disable confirmation dialog interactions
yesBtn.interactive = false;
noBtn.interactive = false;
- // Only show back button in AI mode, not in two player mode
- if (mode === 'ai') {
- backBtn.alpha = 1;
- backBtn.interactive = true;
- } else {
- backBtn.alpha = 0;
- backBtn.interactive = false;
- }
+ // Hide back button in all game modes once game starts
+ backBtn.alpha = 0;
+ backBtn.interactive = false;
exitBtn.alpha = 1;
// Enable exit button interaction
exitBtn.interactive = true;
// In AI mode, player (1) always starts first