/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var ChessPiece = Container.expand(function (type, color, row, col) { var self = Container.call(this); self.pieceType = type; self.pieceColor = color; self.boardRow = row; self.boardCol = col; self.hasMoved = false; var assetId = color + type.charAt(0).toUpperCase() + type.slice(1); var pieceGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.updateBoardPosition = function (newRow, newCol) { self.boardRow = newRow; self.boardCol = newCol; self.hasMoved = true; }; return self; }); var ChessSquare = Container.expand(function (row, col) { var self = Container.call(this); self.row = row; self.col = col; self.piece = null; self.isHighlighted = false; var isLight = (row + col) % 2 === 0; var squareGraphics = self.attachAsset(isLight ? 'lightSquare' : 'darkSquare', { anchorX: 0.5, anchorY: 0.5 }); self.highlight = function () { if (!self.isHighlighted) { var highlight = LK.getAsset('highlightSquare', { anchorX: 0.5, anchorY: 0.5, alpha: 0, scaleX: 0.5, scaleY: 0.5 }); self.addChild(highlight); self.isHighlighted = true; self.highlightGraphics = highlight; // Animate highlight appearance tween(highlight, { alpha: 0.5, scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } }; self.removeHighlight = function () { if (self.isHighlighted && self.highlightGraphics) { var highlightToRemove = self.highlightGraphics; self.isHighlighted = false; self.highlightGraphics = null; // Animate highlight disappearance tween(highlightToRemove, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 150, easing: tween.easeIn, onFinish: function onFinish() { self.removeChild(highlightToRemove); } }); } }; self.setPiece = function (piece) { if (self.piece) { self.removeChild(self.piece); } self.piece = piece; if (piece) { self.addChild(piece); piece.x = 0; piece.y = 0; } }; return self; }); var DifficultyButton = Container.expand(function (text, difficulty) { var self = Container.call(this); self.difficulty = difficulty; var buttonBg = self.attachAsset('difficultyButton', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(text, { size: 60, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.down = function (x, y, obj) { selectedDifficulty = self.difficulty; startGame(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x8b4513 }); /**** * Game Code ****/ // Sound effects // UI elements // Board squares // Chess pieces - black pieces // Chess pieces - white pieces // Game state variables var gameState = 'difficulty'; // 'difficulty', 'playing', 'gameOver' var selectedDifficulty = 'medium'; var currentPlayer = 'white'; var board = []; var selectedSquare = null; var possibleMoves = []; var difficultyButtons = []; var draggedPiece = null; var dragOffset = { x: 0, y: 0 }; var aiHasMoved = false; var capturedWhitePieces = []; var capturedBlackPieces = []; var capturedPiecesContainer = null; var checkedKing = null; // Track the currently checked king for highlighting // Create explosion effect for captures function createExplosionEffect(x, y) { var particles = []; var numParticles = 6; // Reduced by 50% var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844, 0xff6666]; // Explosive colors // Create main explosion particles for (var i = 0; i < numParticles; i++) { var particle = LK.getAsset('highlightSquare', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2 + Math.random() * 0.4, // Varied sizes scaleY: 0.2 + Math.random() * 0.4, alpha: 0.9 }); particle.x = x; particle.y = y; particle.tint = colors[Math.floor(Math.random() * colors.length)]; // Random explosion color game.addChild(particle); particles.push(particle); // Random direction for each particle with slightly reduced variation var angle = i / numParticles * Math.PI * 2 + (Math.random() - 0.5) * 0.5; var speed = 28 + Math.random() * 40; // Reduced speeds by 50% (28-68 range) var targetX = x + Math.cos(angle) * speed; var targetY = y + Math.sin(angle) * speed; // Add rotation and bounce effect var rotationSpeed = (Math.random() - 0.5) * 8; // Initial burst animation with rotation tween(particle, { x: targetX, y: targetY, rotation: rotationSpeed, scaleX: 0.8 + Math.random() * 0.4, scaleY: 0.8 + Math.random() * 0.4 }, { duration: 200, easing: tween.easeOut, onFinish: function (currentParticle) { // Second phase: fade and shrink tween(currentParticle, { scaleX: 0, scaleY: 0, alpha: 0, rotation: currentParticle.rotation + rotationSpeed * 2 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { game.removeChild(currentParticle); } }); }.bind(null, particle) }); } // Create additional flash effect var flashRing = LK.getAsset('highlightSquare', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 1 }); flashRing.x = x; flashRing.y = y; flashRing.tint = 0xffffff; // Bright white flash game.addChild(flashRing); // Flash ring expansion and fade (slightly smaller) tween(flashRing, { scaleX: 1.25, // Reduced by 50% scaleY: 1.25, // Reduced by 50% alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(flashRing); } }); // Create sparks effect (reduced count) for (var j = 0; j < 3; j++) { // Reduced by 50% var spark = LK.getAsset('highlightSquare', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 1 }); spark.x = x; spark.y = y; spark.tint = 0xffff88; // Bright yellow sparks game.addChild(spark); var sparkAngle = Math.random() * Math.PI * 2; var sparkDistance = 20 + Math.random() * 24; // Reduced by 50% (20-44 range) var sparkTargetX = x + Math.cos(sparkAngle) * sparkDistance; var sparkTargetY = y + Math.sin(sparkAngle) * sparkDistance; // Animate sparks with trail effect tween(spark, { x: sparkTargetX, y: sparkTargetY, scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 300 + Math.random() * 200, easing: tween.easeOut, onFinish: function (currentSpark) { game.removeChild(currentSpark); }.bind(null, spark) }); } } // Highlight the king when in check function highlightCheckedKing(color) { // Find the king of the specified color for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceType === 'king' && piece.pieceColor === color) { checkedKing = piece; // Apply red tint to the king with pulsing animation tween(piece, { tint: 0xff0000 }, { duration: 300, easing: tween.easeOut }); // Add pulsing scale effect to make it more visible tween(piece, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(piece, { scaleX: 1.0, scaleY: 1.0 }, { duration: 500, easing: tween.easeInOut }); } }); return; } } } } // Remove highlight from king function removeKingHighlight() { if (checkedKing) { // Reset tint and scale to normal tween(checkedKing, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeOut }); checkedKing = null; } } // Update captured pieces display function updateCapturedPiecesDisplay() { // Clear existing captured pieces display (except label) while (capturedPiecesContainer.children.length > 1) { capturedPiecesContainer.removeChild(capturedPiecesContainer.children[1]); } var yOffset = 20; var pieceSize = 80; var spacing = 90; // Display captured white pieces for (var i = 0; i < capturedWhitePieces.length; i++) { var piece = capturedWhitePieces[i]; var displayPiece = new ChessPiece(piece.pieceType, piece.pieceColor, 0, 0); displayPiece.x = i % 8 * spacing; displayPiece.y = yOffset + Math.floor(i / 8) * spacing; displayPiece.scaleX = 0.8; displayPiece.scaleY = 0.8; capturedPiecesContainer.addChild(displayPiece); } // Display captured black pieces below white pieces var blackStartY = yOffset + Math.ceil(capturedWhitePieces.length / 8) * spacing + 30; for (var j = 0; j < capturedBlackPieces.length; j++) { var piece = capturedBlackPieces[j]; var displayPiece = new ChessPiece(piece.pieceType, piece.pieceColor, 0, 0); displayPiece.x = j % 8 * spacing; displayPiece.y = blackStartY + Math.floor(j / 8) * spacing; displayPiece.scaleX = 0.8; displayPiece.scaleY = 0.8; capturedPiecesContainer.addChild(displayPiece); } } // Board constants var BOARD_SIZE = 8; var SQUARE_SIZE = 230; var BOARD_START_X = 2048 / 2 - BOARD_SIZE * SQUARE_SIZE / 2; var BOARD_START_Y = 2732 / 2 - BOARD_SIZE * SQUARE_SIZE / 2; // Initialize difficulty selection function initializeDifficultySelection() { var easyButton = new DifficultyButton('EASY', 'easy'); easyButton.x = 2048 / 2; easyButton.y = 2732 / 2 - 200; game.addChild(easyButton); difficultyButtons.push(easyButton); var mediumButton = new DifficultyButton('MEDIUM', 'medium'); mediumButton.x = 2048 / 2; mediumButton.y = 2732 / 2; game.addChild(mediumButton); difficultyButtons.push(mediumButton); var hardButton = new DifficultyButton('HARD', 'hard'); hardButton.x = 2048 / 2; hardButton.y = 2732 / 2 + 200; game.addChild(hardButton); difficultyButtons.push(hardButton); var titleText = new Text2('Select Difficulty', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 2 - 400; game.addChild(titleText); } // Start the game after difficulty selection function startGame() { // Remove difficulty buttons for (var i = 0; i < difficultyButtons.length; i++) { game.removeChild(difficultyButtons[i]); } difficultyButtons = []; // Remove title for (var j = game.children.length - 1; j >= 0; j--) { var child = game.children[j]; if (child instanceof Text2) { game.removeChild(child); } } gameState = 'playing'; initializeBoard(); setupInitialPieces(); // Initialize captured pieces area capturedPiecesContainer = new Container(); capturedPiecesContainer.x = 350; capturedPiecesContainer.y = 150; game.addChild(capturedPiecesContainer); // Add labels for captured pieces var capturedLabel = new Text2('Captured Pieces', { size: 70, fill: 0xFFFFFF }); capturedLabel.anchor.set(0, 0); capturedLabel.x = 0; capturedLabel.y = -120; capturedPiecesContainer.addChild(capturedLabel); } // Initialize the chess board function initializeBoard() { board = []; for (var row = 0; row < BOARD_SIZE; row++) { board[row] = []; for (var col = 0; col < BOARD_SIZE; col++) { var square = new ChessSquare(row, col); square.x = BOARD_START_X + col * SQUARE_SIZE + SQUARE_SIZE / 2; square.y = BOARD_START_Y + row * SQUARE_SIZE + SQUARE_SIZE / 2; game.addChild(square); board[row][col] = square; } } } // Setup initial piece positions function setupInitialPieces() { var pieceOrder = ['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook']; // Black pieces (top of board) for (var col = 0; col < 8; col++) { var blackPiece = new ChessPiece(pieceOrder[col], 'black', 0, col); board[0][col].setPiece(blackPiece); var blackPawn = new ChessPiece('pawn', 'black', 1, col); board[1][col].setPiece(blackPawn); } // White pieces (bottom of board) for (var col = 0; col < 8; col++) { var whitePawn = new ChessPiece('pawn', 'white', 6, col); board[6][col].setPiece(whitePawn); var whitePiece = new ChessPiece(pieceOrder[col], 'white', 7, col); board[7][col].setPiece(whitePiece); } } // Get all pseudo-legal moves for a piece (ignores pins and king safety) function getRawMoves(piece, fromRow, fromCol) { var moves = []; var type = piece.pieceType; var color = piece.pieceColor; if (type === 'pawn') { var direction = color === 'white' ? -1 : 1; var startRow = color === 'white' ? 6 : 1; // Move forward 1 square (only if empty) if (isValidPosition(fromRow + direction, fromCol) && !board[fromRow + direction][fromCol].piece) { moves.push({ row: fromRow + direction, col: fromCol }); // Move forward 2 squares from starting position (only if both squares are empty) if (fromRow === startRow && isValidPosition(fromRow + 2 * direction, fromCol) && !board[fromRow + 2 * direction][fromCol].piece) { moves.push({ row: fromRow + 2 * direction, col: fromCol }); } } // Pawn captures diagonally (only if there's an opponent piece) for (var dc = -1; dc <= 1; dc += 2) { if (isValidPosition(fromRow + direction, fromCol + dc)) { var targetPiece = board[fromRow + direction][fromCol + dc].piece; if (targetPiece && targetPiece.pieceColor !== color) { moves.push({ row: fromRow + direction, col: fromCol + dc }); } } } } else if (type === 'rook') { var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]; for (var d = 0; d < directions.length; d++) { var dr = directions[d][0]; var dc = directions[d][1]; for (var i = 1; i < 8; i++) { var newRow = fromRow + dr * i; var newCol = fromCol + dc * i; if (!isValidPosition(newRow, newCol)) break; moves.push({ row: newRow, col: newCol }); var targetPiece = board[newRow][newCol].piece; if (targetPiece) { break; } } } } else if (type === 'bishop') { var directions = [[1, 1], [1, -1], [-1, 1], [-1, -1]]; for (var d = 0; d < directions.length; d++) { var dr = directions[d][0]; var dc = directions[d][1]; for (var i = 1; i < 8; i++) { var newRow = fromRow + dr * i; var newCol = fromCol + dc * i; if (!isValidPosition(newRow, newCol)) break; moves.push({ row: newRow, col: newCol }); var targetPiece = board[newRow][newCol].piece; if (targetPiece) { break; } } } } else if (type === 'queen') { var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]; for (var d = 0; d < directions.length; d++) { var dr = directions[d][0]; var dc = directions[d][1]; for (var i = 1; i < 8; i++) { var newRow = fromRow + dr * i; var newCol = fromCol + dc * i; if (!isValidPosition(newRow, newCol)) break; moves.push({ row: newRow, col: newCol }); var targetPiece = board[newRow][newCol].piece; if (targetPiece) { break; } } } } else if (type === 'king') { var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]; for (var d = 0; d < directions.length; d++) { var newRow = fromRow + directions[d][0]; var newCol = fromCol + directions[d][1]; if (isValidPosition(newRow, newCol)) { moves.push({ row: newRow, col: newCol }); } } } else if (type === 'knight') { var knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]]; for (var m = 0; m < knightMoves.length; m++) { var newRow = fromRow + knightMoves[m][0]; var newCol = fromCol + knightMoves[m][1]; if (isValidPosition(newRow, newCol)) { moves.push({ row: newRow, col: newCol }); } } } return moves; } // Get legal moves for a piece function getLegalMoves(piece, fromRow, fromCol) { var moves = []; var type = piece.pieceType; var color = piece.pieceColor; if (type === 'pawn') { var direction = color === 'white' ? -1 : 1; var startRow = color === 'white' ? 6 : 1; // Forward move if (isValidPosition(fromRow + direction, fromCol) && !board[fromRow + direction][fromCol].piece) { moves.push({ row: fromRow + direction, col: fromCol }); // Two squares forward from starting position if (fromRow === startRow && !board[fromRow + 2 * direction][fromCol].piece) { moves.push({ row: fromRow + 2 * direction, col: fromCol }); } } // Diagonal captures for (var dc = -1; dc <= 1; dc += 2) { if (isValidPosition(fromRow + direction, fromCol + dc)) { var targetPiece = board[fromRow + direction][fromCol + dc].piece; if (targetPiece && targetPiece.pieceColor !== color) { moves.push({ row: fromRow + direction, col: fromCol + dc }); } } } } else if (type === 'rook') { var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]; for (var d = 0; d < directions.length; d++) { var dr = directions[d][0]; var dc = directions[d][1]; for (var i = 1; i < 8; i++) { var newRow = fromRow + dr * i; var newCol = fromCol + dc * i; if (!isValidPosition(newRow, newCol)) break; var targetPiece = board[newRow][newCol].piece; if (targetPiece) { if (targetPiece.pieceColor !== color) { moves.push({ row: newRow, col: newCol }); } break; } else { moves.push({ row: newRow, col: newCol }); } } } } else if (type === 'bishop') { var directions = [[1, 1], [1, -1], [-1, 1], [-1, -1]]; for (var d = 0; d < directions.length; d++) { var dr = directions[d][0]; var dc = directions[d][1]; for (var i = 1; i < 8; i++) { var newRow = fromRow + dr * i; var newCol = fromCol + dc * i; if (!isValidPosition(newRow, newCol)) break; var targetPiece = board[newRow][newCol].piece; if (targetPiece) { if (targetPiece.pieceColor !== color) { moves.push({ row: newRow, col: newCol }); } break; } else { moves.push({ row: newRow, col: newCol }); } } } } else if (type === 'queen') { var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]; for (var d = 0; d < directions.length; d++) { var dr = directions[d][0]; var dc = directions[d][1]; for (var i = 1; i < 8; i++) { var newRow = fromRow + dr * i; var newCol = fromCol + dc * i; if (!isValidPosition(newRow, newCol)) break; var targetPiece = board[newRow][newCol].piece; if (targetPiece) { if (targetPiece.pieceColor !== color) { moves.push({ row: newRow, col: newCol }); } break; } else { moves.push({ row: newRow, col: newCol }); } } } } else if (type === 'king') { var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]; for (var d = 0; d < directions.length; d++) { var newRow = fromRow + directions[d][0]; var newCol = fromCol + directions[d][1]; if (isValidPosition(newRow, newCol)) { var targetPiece = board[newRow][newCol].piece; if (!targetPiece || targetPiece.pieceColor !== color) { moves.push({ row: newRow, col: newCol }); } } } } else if (type === 'knight') { var knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]]; for (var m = 0; m < knightMoves.length; m++) { var newRow = fromRow + knightMoves[m][0]; var newCol = fromCol + knightMoves[m][1]; if (isValidPosition(newRow, newCol)) { var targetPiece = board[newRow][newCol].piece; if (!targetPiece || targetPiece.pieceColor !== color) { moves.push({ row: newRow, col: newCol }); } } } } // Filter out moves that would leave own king in check (pin rule) var legalMoves = []; var _loop = function _loop() { move = moves[i]; // Simulate the move fromPiece = board[fromRow][fromCol].piece; toPiece = board[move.row][move.col].piece; // Temporarily move the piece board[fromRow][fromCol].setPiece(null); board[move.row][move.col].setPiece(fromPiece); kingSafe = true; // Find own king position after move kingRow = -1, kingCol = -1; for (r = 0; r < BOARD_SIZE; r++) { for (c = 0; c < BOARD_SIZE; c++) { p = board[r][c].piece; if (p && p.pieceType === 'king' && p.pieceColor === color) { kingRow = r; kingCol = c; } } } // If this piece is the king, update kingRow/kingCol to new position if (type === 'king') { kingRow = move.row; kingCol = move.col; } // Check if any enemy piece can attack the king after this move for (r = 0; r < BOARD_SIZE; r++) { for (c = 0; c < BOARD_SIZE; c++) { enemy = board[r][c].piece; if (enemy && enemy.pieceColor !== color) { enemyMoves = getRawMoves(enemy, r, c); for (j = 0; j < enemyMoves.length; j++) { if (enemyMoves[j].row === kingRow && enemyMoves[j].col === kingCol) { kingSafe = false; break; } } } if (!kingSafe) break; } if (!kingSafe) break; } // Undo the move board[move.row][move.col].setPiece(toPiece); board[fromRow][fromCol].setPiece(fromPiece); if (kingSafe) { legalMoves.push(move); } }, move, fromPiece, toPiece, kingSafe, kingRow, kingCol, r, c, p, r, c, enemy, enemyMoves, j; for (var i = 0; i < moves.length; i++) { _loop(); } return legalMoves; } // Check if position is valid on board function isValidPosition(row, col) { return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE; } // Highlight possible moves function highlightMoves(moves) { for (var i = 0; i < moves.length; i++) { board[moves[i].row][moves[i].col].highlight(); } } // Clear all highlights function clearHighlights() { for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { board[row][col].removeHighlight(); } } } // Make a move function makeMove(fromRow, fromCol, toRow, toCol) { var piece = board[fromRow][fromCol].piece; var capturedPiece = board[toRow][toCol].piece; // Prevent capturing the king if (capturedPiece && capturedPiece.pieceType === 'king') { // Invalid move: cannot capture king, revert and return // Return piece to original position if dragged if (draggedPiece) { game.removeChild(draggedPiece); board[fromRow][fromCol].setPiece(draggedPiece); } clearHighlights(); draggedPiece = null; selectedSquare = null; possibleMoves = []; return; } // Calculate target position var targetX = BOARD_START_X + toCol * SQUARE_SIZE + SQUARE_SIZE / 2; var targetY = BOARD_START_Y + toRow * SQUARE_SIZE + SQUARE_SIZE / 2; // Check if this is an AI move (piece is black and not being dragged) var isAIMove = piece.pieceColor === 'black' && !draggedPiece; if (isAIMove) { // For AI moves, animate the piece movement // First, bring piece to game level for animation var currentSquare = board[fromRow][fromCol]; var currentGlobalPos = currentSquare.toGlobal(piece.position); currentSquare.removeChild(piece); game.addChild(piece); piece.x = currentGlobalPos.x; piece.y = currentGlobalPos.y; // Remove piece from old position on board board[fromRow][fromCol].setPiece(null); // Handle captured piece animation if there's a capture if (capturedPiece) { // Create explosion effect at capture location var explosionX = BOARD_START_X + toCol * SQUARE_SIZE + SQUARE_SIZE / 2; var explosionY = BOARD_START_Y + toRow * SQUARE_SIZE + SQUARE_SIZE / 2; createExplosionEffect(explosionX, explosionY); // Add to captured pieces array if (capturedPiece.pieceColor === 'white') { capturedWhitePieces.push({ pieceType: capturedPiece.pieceType, pieceColor: capturedPiece.pieceColor }); } else { capturedBlackPieces.push({ pieceType: capturedPiece.pieceType, pieceColor: capturedPiece.pieceColor }); } // Remove captured piece from board immediately board[toRow][toCol].setPiece(null); // Animate captured piece moving to captured area var capturedTargetX = capturedPiecesContainer.x; var capturedTargetY = capturedPiecesContainer.y + 100; // First animate to captured area, then scale down tween(capturedPiece, { x: capturedTargetX, y: capturedTargetY, scaleX: 0.6, scaleY: 0.6 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Then fade out and destroy tween(capturedPiece, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { // Destroy the captured piece and update display capturedPiece.destroy(); updateCapturedPiecesDisplay(); } }); } }); } // Set piece in new position but don't add to square yet board[toRow][toCol].piece = piece; piece.updateBoardPosition(toRow, toCol); // Animate piece to target position tween(piece, { x: targetX, y: targetY }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Move piece from game to target square game.removeChild(piece); board[toRow][toCol].setPiece(piece); // Play sound after animation if (capturedPiece) { LK.getSound('captureSound').play(); } else { LK.getSound('moveSound').play(); } // Check for pawn promotion if (piece.pieceType === 'pawn') { if (piece.pieceColor === 'white' && toRow === 0 || piece.pieceColor === 'black' && toRow === 7) { // Promote to queen board[toRow][toCol].setPiece(null); var promotedQueen = new ChessPiece('queen', piece.pieceColor, toRow, toCol); board[toRow][toCol].setPiece(promotedQueen); } } // Switch turns currentPlayer = currentPlayer === 'white' ? 'black' : 'white'; // Remove previous king highlight if king is no longer in check if (checkedKing && !isInCheck(checkedKing.pieceColor)) { removeKingHighlight(); } // Reset AI move flag when switching turns if (currentPlayer === 'black') { aiHasMoved = false; } // Reset dragged piece to null to re-enable piece interaction draggedPiece = null; // Check for game end conditions first if (isCheckmate(currentPlayer)) { // Highlight the checkmated king highlightCheckedKing(currentPlayer); // Play checkmate sound LK.getSound('checkmateSound').play(); // Create rainbow checkmate text var checkmateText = new Text2('Check Mate!', { size: 150, fill: 0xff0000 // Start with red }); checkmateText.anchor.set(0.5, 0.5); checkmateText.x = 2048 / 2; checkmateText.y = 300; game.addChild(checkmateText); // Rainbow color animation - cycle through colors var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff]; var colorIndex = 0; var colorTimer = LK.setInterval(function () { colorIndex = (colorIndex + 1) % colors.length; checkmateText.tint = colors[colorIndex]; }, 100); // Fade out the text slowly tween(checkmateText, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { LK.clearInterval(colorTimer); game.removeChild(checkmateText); } }); // End game 1 second after checkmate text appears LK.setTimeout(function () { if (currentPlayer === 'white') { LK.showGameOver(); } else { LK.showYouWin(); } gameState = 'gameOver'; }, 1000); } else if (isStalemate(currentPlayer)) { LK.showGameOver(); // Show as draw gameState = 'gameOver'; } else if (isInCheck(currentPlayer)) { // Highlight the checked king highlightCheckedKing(currentPlayer); // Play check sound LK.getSound('checkSound').play(); // Create rainbow check text var checkText = new Text2('Check!', { size: 120, fill: 0xff0000 // Start with red }); checkText.anchor.set(0.5, 0.5); checkText.x = 2048 / 2; checkText.y = 300; game.addChild(checkText); // Rainbow color animation - cycle through colors var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff]; var colorIndex = 0; var colorTimer = LK.setInterval(function () { colorIndex = (colorIndex + 1) % colors.length; checkText.tint = colors[colorIndex]; }, 100); // Fade out the text slowly tween(checkText, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { LK.clearInterval(colorTimer); game.removeChild(checkText); } }); } } }); } else { // For player moves, animate the piece smoothly to target position // Remove piece from old position board[fromRow][fromCol].setPiece(null); // Set piece in new position but don't add to square yet board[toRow][toCol].piece = piece; piece.updateBoardPosition(toRow, toCol); // Add capture animation if there's a captured piece if (capturedPiece) { // Create explosion effect at capture location createExplosionEffect(targetX, targetY); // Add to captured pieces array if (capturedPiece.pieceColor === 'white') { capturedWhitePieces.push({ pieceType: capturedPiece.pieceType, pieceColor: capturedPiece.pieceColor }); } else { capturedBlackPieces.push({ pieceType: capturedPiece.pieceType, pieceColor: capturedPiece.pieceColor }); } // Animate captured piece moving to captured area first var capturedTargetX = capturedPiecesContainer.x; var capturedTargetY = capturedPiecesContainer.y + 100; tween(capturedPiece, { x: capturedTargetX, y: capturedTargetY, scaleX: 0.6, scaleY: 0.6 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Then scale down the captured piece before removing tween(capturedPiece, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { // Captured piece animation complete updateCapturedPiecesDisplay(); } }); } }); } // Animate piece to target position tween(piece, { x: targetX, y: targetY }, { duration: 250, easing: tween.easeOut, onFinish: function onFinish() { // Move piece from game to target square game.removeChild(piece); board[toRow][toCol].setPiece(piece); // Play sound after animation if (capturedPiece) { LK.getSound('captureSound').play(); } else { LK.getSound('moveSound').play(); } // Check for pawn promotion if (piece.pieceType === 'pawn') { if (piece.pieceColor === 'white' && toRow === 0 || piece.pieceColor === 'black' && toRow === 7) { // Promote to queen board[toRow][toCol].setPiece(null); var promotedQueen = new ChessPiece('queen', piece.pieceColor, toRow, toCol); board[toRow][toCol].setPiece(promotedQueen); } } // Switch turns currentPlayer = currentPlayer === 'white' ? 'black' : 'white'; // Remove previous king highlight if king is no longer in check if (checkedKing && !isInCheck(checkedKing.pieceColor)) { removeKingHighlight(); } // Reset AI move flag when switching turns if (currentPlayer === 'black') { aiHasMoved = false; } // Reset dragged piece to null to re-enable piece interaction draggedPiece = null; // Check for game end conditions first if (isCheckmate(currentPlayer)) { // Highlight the checkmated king highlightCheckedKing(currentPlayer); // Play checkmate sound LK.getSound('checkmateSound').play(); // Create rainbow checkmate text var checkmateText = new Text2('Check Mate!', { size: 150, fill: 0xff0000 // Start with red }); checkmateText.anchor.set(0.5, 0.5); checkmateText.x = 2048 / 2; checkmateText.y = 300; game.addChild(checkmateText); // Rainbow color animation - cycle through colors var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff]; var colorIndex = 0; var colorTimer = LK.setInterval(function () { colorIndex = (colorIndex + 1) % colors.length; checkmateText.tint = colors[colorIndex]; }, 100); // Fade out the text slowly tween(checkmateText, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { LK.clearInterval(colorTimer); game.removeChild(checkmateText); } }); // End game 1 second after checkmate text appears LK.setTimeout(function () { if (currentPlayer === 'white') { LK.showGameOver(); } else { LK.showYouWin(); } gameState = 'gameOver'; }, 1000); } else if (isStalemate(currentPlayer)) { LK.showGameOver(); // Show as draw gameState = 'gameOver'; } else if (isInCheck(currentPlayer)) { // Highlight the checked king highlightCheckedKing(currentPlayer); // Play check sound LK.getSound('checkSound').play(); // Create rainbow check text var checkText = new Text2('Check!', { size: 120, fill: 0xff0000 // Start with red }); checkText.anchor.set(0.5, 0.5); checkText.x = 2048 / 2; checkText.y = 300; game.addChild(checkText); // Rainbow color animation - cycle through colors var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff]; var colorIndex = 0; var colorTimer = LK.setInterval(function () { colorIndex = (colorIndex + 1) % colors.length; checkText.tint = colors[colorIndex]; }, 100); // Fade out the text slowly tween(checkText, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { LK.clearInterval(colorTimer); game.removeChild(checkText); } }); } } }); } } // Check if a king is in check function isInCheck(color) { // Find king position var kingPos = null; for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceType === 'king' && piece.pieceColor === color) { kingPos = { row: row, col: col }; break; } } if (kingPos) break; } if (!kingPos) return false; // No king found // Check if any enemy piece can attack the king for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceColor !== color) { var moves = getRawMoves(piece, row, col); for (var i = 0; i < moves.length; i++) { if (moves[i].row === kingPos.row && moves[i].col === kingPos.col) { return true; // King is under attack } } } } } return false; // King is safe } // Simple checkmate detection - king must be in check AND have no legal moves function isCheckmate(color) { // First check if king is in check if (!isInCheck(color)) { return false; // Not checkmate if king is not in check } // Find king var kingPos = null; for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceType === 'king' && piece.pieceColor === color) { kingPos = { row: row, col: col }; break; } } if (kingPos) break; } if (!kingPos) return true; // No king found // Check if any piece can make a legal move for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceColor === color) { var moves = getLegalMoves(piece, row, col); if (moves.length > 0) { return false; // Found a legal move } } } } return true; // No legal moves found and king is in check } // Simple stalemate detection - king is NOT in check but has no legal moves function isStalemate(color) { // First check if king is in check if (isInCheck(color)) { return false; // Not stalemate if king is in check } // Check if any piece can make a legal move for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceColor === color) { var moves = getLegalMoves(piece, row, col); if (moves.length > 0) { return false; // Found a legal move } } } } return true; // No legal moves found but king is not in check } // AI move selection based on difficulty function getAIMove() { var allMoves = []; // Get all possible moves for AI (black pieces) for (var row = 0; row < BOARD_SIZE; row++) { for (var col = 0; col < BOARD_SIZE; col++) { var piece = board[row][col].piece; if (piece && piece.pieceColor === 'black') { var moves = getLegalMoves(piece, row, col); for (var m = 0; m < moves.length; m++) { // Prevent AI from selecting a move that would capture the king var target = board[moves[m].row][moves[m].col].piece; if (target && target.pieceType === 'king') { continue; } allMoves.push({ fromRow: row, fromCol: col, toRow: moves[m].row, toCol: moves[m].col, piece: piece }); } } } } if (allMoves.length === 0) return null; if (selectedDifficulty === 'easy') { // Random move return allMoves[Math.floor(Math.random() * allMoves.length)]; } else if (selectedDifficulty === 'medium') { // Prefer captures var captures = []; for (var i = 0; i < allMoves.length; i++) { var move = allMoves[i]; if (board[move.toRow][move.toCol].piece) { captures.push(move); } } if (captures.length > 0) { return captures[Math.floor(Math.random() * captures.length)]; } return allMoves[Math.floor(Math.random() * allMoves.length)]; } else { // Hard: prefer captures, then center moves var captures = []; var centerMoves = []; for (var i = 0; i < allMoves.length; i++) { var move = allMoves[i]; if (board[move.toRow][move.toCol].piece) { captures.push(move); } else if (move.toRow >= 3 && move.toRow <= 4 && move.toCol >= 3 && move.toCol <= 4) { centerMoves.push(move); } } if (captures.length > 0) { return captures[Math.floor(Math.random() * captures.length)]; } if (centerMoves.length > 0) { return centerMoves[Math.floor(Math.random() * centerMoves.length)]; } return allMoves[Math.floor(Math.random() * allMoves.length)]; } } // Get board position from screen coordinates function getBoardPosition(x, y) { var col = Math.floor((x - BOARD_START_X) / SQUARE_SIZE); var row = Math.floor((y - BOARD_START_Y) / SQUARE_SIZE); if (row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE) { return { row: row, col: col }; } return null; } // Handle mouse/touch down game.down = function (x, y, obj) { if (gameState !== 'playing' || currentPlayer !== 'white') return; var pos = getBoardPosition(x, y); if (!pos) return; var square = board[pos.row][pos.col]; var piece = square.piece; if (piece && piece.pieceColor === 'white') { // Start dragging draggedPiece = piece; // Set drag offset to zero so piece stays at click position dragOffset.x = 0; dragOffset.y = 0; // Clear previous highlights clearHighlights(); // Highlight possible moves possibleMoves = getLegalMoves(piece, pos.row, pos.col); highlightMoves(possibleMoves); selectedSquare = { row: pos.row, col: pos.col }; // Bring piece to front but keep it at current position var tempParent = piece.parent; var currentGlobalPos = tempParent.toGlobal(piece.position); tempParent.removeChild(piece); game.addChild(piece); piece.x = currentGlobalPos.x; piece.y = currentGlobalPos.y; // Add selection animation tween(piece, { scaleX: 1.1, scaleY: 1.1 }, { duration: 150, easing: tween.easeOut }); } }; // Handle mouse/touch move game.move = function (x, y, obj) { if (draggedPiece) { draggedPiece.x = x; draggedPiece.y = y; } }; // Handle mouse/touch up game.up = function (x, y, obj) { if (!draggedPiece || gameState !== 'playing') return; var pos = getBoardPosition(x, y); var validMove = false; if (pos && selectedSquare) { // Check if this is a valid move for (var i = 0; i < possibleMoves.length; i++) { if (possibleMoves[i].row === pos.row && possibleMoves[i].col === pos.col) { validMove = true; break; } } if (validMove) { // Prevent player from capturing the king var targetPiece = board[pos.row][pos.col].piece; if (targetPiece && targetPiece.pieceType === 'king') { // Invalid move: cannot capture king, revert and return game.removeChild(draggedPiece); board[selectedSquare.row][selectedSquare.col].setPiece(draggedPiece); clearHighlights(); draggedPiece = null; selectedSquare = null; possibleMoves = []; return; } // Make the move makeMove(selectedSquare.row, selectedSquare.col, pos.row, pos.col); // Clear selection state after valid move selectedSquare = null; possibleMoves = []; } } if (!validMove && selectedSquare) { // Return piece to original position with animation var originalSquare = board[selectedSquare.row][selectedSquare.col]; var originalX = BOARD_START_X + selectedSquare.col * SQUARE_SIZE + SQUARE_SIZE / 2; var originalY = BOARD_START_Y + selectedSquare.row * SQUARE_SIZE + SQUARE_SIZE / 2; // Reset scale and animate back to original position tween(draggedPiece, { x: originalX, y: originalY, scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(draggedPiece); originalSquare.setPiece(draggedPiece); // Reset dragging state after animation completes draggedPiece = null; selectedSquare = null; possibleMoves = []; } }); // Clear highlights immediately clearHighlights(); } else { // Reset scale for valid moves if (draggedPiece) { tween(draggedPiece, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOut }); } // Clean up for valid moves clearHighlights(); draggedPiece = null; selectedSquare = null; possibleMoves = []; } }; // Game update loop game.update = function () { if (gameState === 'playing' && currentPlayer === 'black' && !draggedPiece && !aiHasMoved) { // AI turn - only if player is not currently dragging a piece and AI hasn't moved yet if (LK.ticks % 60 === 0) { // Wait 1 second before AI move aiHasMoved = true; // Set flag immediately to prevent multiple moves LK.setTimeout(function () { var aiMove = getAIMove(); if (aiMove) { makeMove(aiMove.fromRow, aiMove.fromCol, aiMove.toRow, aiMove.toCol); } }, 1000); } } }; // Play background music LK.playMusic('Game_music'); // Initialize the game if (gameState === 'difficulty') { initializeDifficultySelection(); }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ChessPiece = Container.expand(function (type, color, row, col) {
var self = Container.call(this);
self.pieceType = type;
self.pieceColor = color;
self.boardRow = row;
self.boardCol = col;
self.hasMoved = false;
var assetId = color + type.charAt(0).toUpperCase() + type.slice(1);
var pieceGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.updateBoardPosition = function (newRow, newCol) {
self.boardRow = newRow;
self.boardCol = newCol;
self.hasMoved = true;
};
return self;
});
var ChessSquare = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.piece = null;
self.isHighlighted = false;
var isLight = (row + col) % 2 === 0;
var squareGraphics = self.attachAsset(isLight ? 'lightSquare' : 'darkSquare', {
anchorX: 0.5,
anchorY: 0.5
});
self.highlight = function () {
if (!self.isHighlighted) {
var highlight = LK.getAsset('highlightSquare', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
});
self.addChild(highlight);
self.isHighlighted = true;
self.highlightGraphics = highlight;
// Animate highlight appearance
tween(highlight, {
alpha: 0.5,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.removeHighlight = function () {
if (self.isHighlighted && self.highlightGraphics) {
var highlightToRemove = self.highlightGraphics;
self.isHighlighted = false;
self.highlightGraphics = null;
// Animate highlight disappearance
tween(highlightToRemove, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
self.removeChild(highlightToRemove);
}
});
}
};
self.setPiece = function (piece) {
if (self.piece) {
self.removeChild(self.piece);
}
self.piece = piece;
if (piece) {
self.addChild(piece);
piece.x = 0;
piece.y = 0;
}
};
return self;
});
var DifficultyButton = Container.expand(function (text, difficulty) {
var self = Container.call(this);
self.difficulty = difficulty;
var buttonBg = self.attachAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
selectedDifficulty = self.difficulty;
startGame();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8b4513
});
/****
* Game Code
****/
// Sound effects
// UI elements
// Board squares
// Chess pieces - black pieces
// Chess pieces - white pieces
// Game state variables
var gameState = 'difficulty'; // 'difficulty', 'playing', 'gameOver'
var selectedDifficulty = 'medium';
var currentPlayer = 'white';
var board = [];
var selectedSquare = null;
var possibleMoves = [];
var difficultyButtons = [];
var draggedPiece = null;
var dragOffset = {
x: 0,
y: 0
};
var aiHasMoved = false;
var capturedWhitePieces = [];
var capturedBlackPieces = [];
var capturedPiecesContainer = null;
var checkedKing = null; // Track the currently checked king for highlighting
// Create explosion effect for captures
function createExplosionEffect(x, y) {
var particles = [];
var numParticles = 6; // Reduced by 50%
var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844, 0xff6666]; // Explosive colors
// Create main explosion particles
for (var i = 0; i < numParticles; i++) {
var particle = LK.getAsset('highlightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + Math.random() * 0.4,
// Varied sizes
scaleY: 0.2 + Math.random() * 0.4,
alpha: 0.9
});
particle.x = x;
particle.y = y;
particle.tint = colors[Math.floor(Math.random() * colors.length)]; // Random explosion color
game.addChild(particle);
particles.push(particle);
// Random direction for each particle with slightly reduced variation
var angle = i / numParticles * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var speed = 28 + Math.random() * 40; // Reduced speeds by 50% (28-68 range)
var targetX = x + Math.cos(angle) * speed;
var targetY = y + Math.sin(angle) * speed;
// Add rotation and bounce effect
var rotationSpeed = (Math.random() - 0.5) * 8;
// Initial burst animation with rotation
tween(particle, {
x: targetX,
y: targetY,
rotation: rotationSpeed,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.8 + Math.random() * 0.4
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function (currentParticle) {
// Second phase: fade and shrink
tween(currentParticle, {
scaleX: 0,
scaleY: 0,
alpha: 0,
rotation: currentParticle.rotation + rotationSpeed * 2
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(currentParticle);
}
});
}.bind(null, particle)
});
}
// Create additional flash effect
var flashRing = LK.getAsset('highlightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
});
flashRing.x = x;
flashRing.y = y;
flashRing.tint = 0xffffff; // Bright white flash
game.addChild(flashRing);
// Flash ring expansion and fade (slightly smaller)
tween(flashRing, {
scaleX: 1.25,
// Reduced by 50%
scaleY: 1.25,
// Reduced by 50%
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(flashRing);
}
});
// Create sparks effect (reduced count)
for (var j = 0; j < 3; j++) {
// Reduced by 50%
var spark = LK.getAsset('highlightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
});
spark.x = x;
spark.y = y;
spark.tint = 0xffff88; // Bright yellow sparks
game.addChild(spark);
var sparkAngle = Math.random() * Math.PI * 2;
var sparkDistance = 20 + Math.random() * 24; // Reduced by 50% (20-44 range)
var sparkTargetX = x + Math.cos(sparkAngle) * sparkDistance;
var sparkTargetY = y + Math.sin(sparkAngle) * sparkDistance;
// Animate sparks with trail effect
tween(spark, {
x: sparkTargetX,
y: sparkTargetY,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function (currentSpark) {
game.removeChild(currentSpark);
}.bind(null, spark)
});
}
}
// Highlight the king when in check
function highlightCheckedKing(color) {
// Find the king of the specified color
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceType === 'king' && piece.pieceColor === color) {
checkedKing = piece;
// Apply red tint to the king with pulsing animation
tween(piece, {
tint: 0xff0000
}, {
duration: 300,
easing: tween.easeOut
});
// Add pulsing scale effect to make it more visible
tween(piece, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(piece, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
return;
}
}
}
}
// Remove highlight from king
function removeKingHighlight() {
if (checkedKing) {
// Reset tint and scale to normal
tween(checkedKing, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
checkedKing = null;
}
}
// Update captured pieces display
function updateCapturedPiecesDisplay() {
// Clear existing captured pieces display (except label)
while (capturedPiecesContainer.children.length > 1) {
capturedPiecesContainer.removeChild(capturedPiecesContainer.children[1]);
}
var yOffset = 20;
var pieceSize = 80;
var spacing = 90;
// Display captured white pieces
for (var i = 0; i < capturedWhitePieces.length; i++) {
var piece = capturedWhitePieces[i];
var displayPiece = new ChessPiece(piece.pieceType, piece.pieceColor, 0, 0);
displayPiece.x = i % 8 * spacing;
displayPiece.y = yOffset + Math.floor(i / 8) * spacing;
displayPiece.scaleX = 0.8;
displayPiece.scaleY = 0.8;
capturedPiecesContainer.addChild(displayPiece);
}
// Display captured black pieces below white pieces
var blackStartY = yOffset + Math.ceil(capturedWhitePieces.length / 8) * spacing + 30;
for (var j = 0; j < capturedBlackPieces.length; j++) {
var piece = capturedBlackPieces[j];
var displayPiece = new ChessPiece(piece.pieceType, piece.pieceColor, 0, 0);
displayPiece.x = j % 8 * spacing;
displayPiece.y = blackStartY + Math.floor(j / 8) * spacing;
displayPiece.scaleX = 0.8;
displayPiece.scaleY = 0.8;
capturedPiecesContainer.addChild(displayPiece);
}
}
// Board constants
var BOARD_SIZE = 8;
var SQUARE_SIZE = 230;
var BOARD_START_X = 2048 / 2 - BOARD_SIZE * SQUARE_SIZE / 2;
var BOARD_START_Y = 2732 / 2 - BOARD_SIZE * SQUARE_SIZE / 2;
// Initialize difficulty selection
function initializeDifficultySelection() {
var easyButton = new DifficultyButton('EASY', 'easy');
easyButton.x = 2048 / 2;
easyButton.y = 2732 / 2 - 200;
game.addChild(easyButton);
difficultyButtons.push(easyButton);
var mediumButton = new DifficultyButton('MEDIUM', 'medium');
mediumButton.x = 2048 / 2;
mediumButton.y = 2732 / 2;
game.addChild(mediumButton);
difficultyButtons.push(mediumButton);
var hardButton = new DifficultyButton('HARD', 'hard');
hardButton.x = 2048 / 2;
hardButton.y = 2732 / 2 + 200;
game.addChild(hardButton);
difficultyButtons.push(hardButton);
var titleText = new Text2('Select Difficulty', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 2 - 400;
game.addChild(titleText);
}
// Start the game after difficulty selection
function startGame() {
// Remove difficulty buttons
for (var i = 0; i < difficultyButtons.length; i++) {
game.removeChild(difficultyButtons[i]);
}
difficultyButtons = [];
// Remove title
for (var j = game.children.length - 1; j >= 0; j--) {
var child = game.children[j];
if (child instanceof Text2) {
game.removeChild(child);
}
}
gameState = 'playing';
initializeBoard();
setupInitialPieces();
// Initialize captured pieces area
capturedPiecesContainer = new Container();
capturedPiecesContainer.x = 350;
capturedPiecesContainer.y = 150;
game.addChild(capturedPiecesContainer);
// Add labels for captured pieces
var capturedLabel = new Text2('Captured Pieces', {
size: 70,
fill: 0xFFFFFF
});
capturedLabel.anchor.set(0, 0);
capturedLabel.x = 0;
capturedLabel.y = -120;
capturedPiecesContainer.addChild(capturedLabel);
}
// Initialize the chess board
function initializeBoard() {
board = [];
for (var row = 0; row < BOARD_SIZE; row++) {
board[row] = [];
for (var col = 0; col < BOARD_SIZE; col++) {
var square = new ChessSquare(row, col);
square.x = BOARD_START_X + col * SQUARE_SIZE + SQUARE_SIZE / 2;
square.y = BOARD_START_Y + row * SQUARE_SIZE + SQUARE_SIZE / 2;
game.addChild(square);
board[row][col] = square;
}
}
}
// Setup initial piece positions
function setupInitialPieces() {
var pieceOrder = ['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook'];
// Black pieces (top of board)
for (var col = 0; col < 8; col++) {
var blackPiece = new ChessPiece(pieceOrder[col], 'black', 0, col);
board[0][col].setPiece(blackPiece);
var blackPawn = new ChessPiece('pawn', 'black', 1, col);
board[1][col].setPiece(blackPawn);
}
// White pieces (bottom of board)
for (var col = 0; col < 8; col++) {
var whitePawn = new ChessPiece('pawn', 'white', 6, col);
board[6][col].setPiece(whitePawn);
var whitePiece = new ChessPiece(pieceOrder[col], 'white', 7, col);
board[7][col].setPiece(whitePiece);
}
}
// Get all pseudo-legal moves for a piece (ignores pins and king safety)
function getRawMoves(piece, fromRow, fromCol) {
var moves = [];
var type = piece.pieceType;
var color = piece.pieceColor;
if (type === 'pawn') {
var direction = color === 'white' ? -1 : 1;
var startRow = color === 'white' ? 6 : 1;
// Move forward 1 square (only if empty)
if (isValidPosition(fromRow + direction, fromCol) && !board[fromRow + direction][fromCol].piece) {
moves.push({
row: fromRow + direction,
col: fromCol
});
// Move forward 2 squares from starting position (only if both squares are empty)
if (fromRow === startRow && isValidPosition(fromRow + 2 * direction, fromCol) && !board[fromRow + 2 * direction][fromCol].piece) {
moves.push({
row: fromRow + 2 * direction,
col: fromCol
});
}
}
// Pawn captures diagonally (only if there's an opponent piece)
for (var dc = -1; dc <= 1; dc += 2) {
if (isValidPosition(fromRow + direction, fromCol + dc)) {
var targetPiece = board[fromRow + direction][fromCol + dc].piece;
if (targetPiece && targetPiece.pieceColor !== color) {
moves.push({
row: fromRow + direction,
col: fromCol + dc
});
}
}
}
} else if (type === 'rook') {
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
for (var d = 0; d < directions.length; d++) {
var dr = directions[d][0];
var dc = directions[d][1];
for (var i = 1; i < 8; i++) {
var newRow = fromRow + dr * i;
var newCol = fromCol + dc * i;
if (!isValidPosition(newRow, newCol)) break;
moves.push({
row: newRow,
col: newCol
});
var targetPiece = board[newRow][newCol].piece;
if (targetPiece) {
break;
}
}
}
} else if (type === 'bishop') {
var directions = [[1, 1], [1, -1], [-1, 1], [-1, -1]];
for (var d = 0; d < directions.length; d++) {
var dr = directions[d][0];
var dc = directions[d][1];
for (var i = 1; i < 8; i++) {
var newRow = fromRow + dr * i;
var newCol = fromCol + dc * i;
if (!isValidPosition(newRow, newCol)) break;
moves.push({
row: newRow,
col: newCol
});
var targetPiece = board[newRow][newCol].piece;
if (targetPiece) {
break;
}
}
}
} else if (type === 'queen') {
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]];
for (var d = 0; d < directions.length; d++) {
var dr = directions[d][0];
var dc = directions[d][1];
for (var i = 1; i < 8; i++) {
var newRow = fromRow + dr * i;
var newCol = fromCol + dc * i;
if (!isValidPosition(newRow, newCol)) break;
moves.push({
row: newRow,
col: newCol
});
var targetPiece = board[newRow][newCol].piece;
if (targetPiece) {
break;
}
}
}
} else if (type === 'king') {
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]];
for (var d = 0; d < directions.length; d++) {
var newRow = fromRow + directions[d][0];
var newCol = fromCol + directions[d][1];
if (isValidPosition(newRow, newCol)) {
moves.push({
row: newRow,
col: newCol
});
}
}
} else if (type === 'knight') {
var knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]];
for (var m = 0; m < knightMoves.length; m++) {
var newRow = fromRow + knightMoves[m][0];
var newCol = fromCol + knightMoves[m][1];
if (isValidPosition(newRow, newCol)) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
return moves;
}
// Get legal moves for a piece
function getLegalMoves(piece, fromRow, fromCol) {
var moves = [];
var type = piece.pieceType;
var color = piece.pieceColor;
if (type === 'pawn') {
var direction = color === 'white' ? -1 : 1;
var startRow = color === 'white' ? 6 : 1;
// Forward move
if (isValidPosition(fromRow + direction, fromCol) && !board[fromRow + direction][fromCol].piece) {
moves.push({
row: fromRow + direction,
col: fromCol
});
// Two squares forward from starting position
if (fromRow === startRow && !board[fromRow + 2 * direction][fromCol].piece) {
moves.push({
row: fromRow + 2 * direction,
col: fromCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
if (isValidPosition(fromRow + direction, fromCol + dc)) {
var targetPiece = board[fromRow + direction][fromCol + dc].piece;
if (targetPiece && targetPiece.pieceColor !== color) {
moves.push({
row: fromRow + direction,
col: fromCol + dc
});
}
}
}
} else if (type === 'rook') {
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
for (var d = 0; d < directions.length; d++) {
var dr = directions[d][0];
var dc = directions[d][1];
for (var i = 1; i < 8; i++) {
var newRow = fromRow + dr * i;
var newCol = fromCol + dc * i;
if (!isValidPosition(newRow, newCol)) break;
var targetPiece = board[newRow][newCol].piece;
if (targetPiece) {
if (targetPiece.pieceColor !== color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
} else {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else if (type === 'bishop') {
var directions = [[1, 1], [1, -1], [-1, 1], [-1, -1]];
for (var d = 0; d < directions.length; d++) {
var dr = directions[d][0];
var dc = directions[d][1];
for (var i = 1; i < 8; i++) {
var newRow = fromRow + dr * i;
var newCol = fromCol + dc * i;
if (!isValidPosition(newRow, newCol)) break;
var targetPiece = board[newRow][newCol].piece;
if (targetPiece) {
if (targetPiece.pieceColor !== color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
} else {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else if (type === 'queen') {
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]];
for (var d = 0; d < directions.length; d++) {
var dr = directions[d][0];
var dc = directions[d][1];
for (var i = 1; i < 8; i++) {
var newRow = fromRow + dr * i;
var newCol = fromCol + dc * i;
if (!isValidPosition(newRow, newCol)) break;
var targetPiece = board[newRow][newCol].piece;
if (targetPiece) {
if (targetPiece.pieceColor !== color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
} else {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else if (type === 'king') {
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]];
for (var d = 0; d < directions.length; d++) {
var newRow = fromRow + directions[d][0];
var newCol = fromCol + directions[d][1];
if (isValidPosition(newRow, newCol)) {
var targetPiece = board[newRow][newCol].piece;
if (!targetPiece || targetPiece.pieceColor !== color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else if (type === 'knight') {
var knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]];
for (var m = 0; m < knightMoves.length; m++) {
var newRow = fromRow + knightMoves[m][0];
var newCol = fromCol + knightMoves[m][1];
if (isValidPosition(newRow, newCol)) {
var targetPiece = board[newRow][newCol].piece;
if (!targetPiece || targetPiece.pieceColor !== color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// Filter out moves that would leave own king in check (pin rule)
var legalMoves = [];
var _loop = function _loop() {
move = moves[i]; // Simulate the move
fromPiece = board[fromRow][fromCol].piece;
toPiece = board[move.row][move.col].piece; // Temporarily move the piece
board[fromRow][fromCol].setPiece(null);
board[move.row][move.col].setPiece(fromPiece);
kingSafe = true; // Find own king position after move
kingRow = -1, kingCol = -1;
for (r = 0; r < BOARD_SIZE; r++) {
for (c = 0; c < BOARD_SIZE; c++) {
p = board[r][c].piece;
if (p && p.pieceType === 'king' && p.pieceColor === color) {
kingRow = r;
kingCol = c;
}
}
}
// If this piece is the king, update kingRow/kingCol to new position
if (type === 'king') {
kingRow = move.row;
kingCol = move.col;
}
// Check if any enemy piece can attack the king after this move
for (r = 0; r < BOARD_SIZE; r++) {
for (c = 0; c < BOARD_SIZE; c++) {
enemy = board[r][c].piece;
if (enemy && enemy.pieceColor !== color) {
enemyMoves = getRawMoves(enemy, r, c);
for (j = 0; j < enemyMoves.length; j++) {
if (enemyMoves[j].row === kingRow && enemyMoves[j].col === kingCol) {
kingSafe = false;
break;
}
}
}
if (!kingSafe) break;
}
if (!kingSafe) break;
}
// Undo the move
board[move.row][move.col].setPiece(toPiece);
board[fromRow][fromCol].setPiece(fromPiece);
if (kingSafe) {
legalMoves.push(move);
}
},
move,
fromPiece,
toPiece,
kingSafe,
kingRow,
kingCol,
r,
c,
p,
r,
c,
enemy,
enemyMoves,
j;
for (var i = 0; i < moves.length; i++) {
_loop();
}
return legalMoves;
}
// Check if position is valid on board
function isValidPosition(row, col) {
return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE;
}
// Highlight possible moves
function highlightMoves(moves) {
for (var i = 0; i < moves.length; i++) {
board[moves[i].row][moves[i].col].highlight();
}
}
// Clear all highlights
function clearHighlights() {
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
board[row][col].removeHighlight();
}
}
}
// Make a move
function makeMove(fromRow, fromCol, toRow, toCol) {
var piece = board[fromRow][fromCol].piece;
var capturedPiece = board[toRow][toCol].piece;
// Prevent capturing the king
if (capturedPiece && capturedPiece.pieceType === 'king') {
// Invalid move: cannot capture king, revert and return
// Return piece to original position if dragged
if (draggedPiece) {
game.removeChild(draggedPiece);
board[fromRow][fromCol].setPiece(draggedPiece);
}
clearHighlights();
draggedPiece = null;
selectedSquare = null;
possibleMoves = [];
return;
}
// Calculate target position
var targetX = BOARD_START_X + toCol * SQUARE_SIZE + SQUARE_SIZE / 2;
var targetY = BOARD_START_Y + toRow * SQUARE_SIZE + SQUARE_SIZE / 2;
// Check if this is an AI move (piece is black and not being dragged)
var isAIMove = piece.pieceColor === 'black' && !draggedPiece;
if (isAIMove) {
// For AI moves, animate the piece movement
// First, bring piece to game level for animation
var currentSquare = board[fromRow][fromCol];
var currentGlobalPos = currentSquare.toGlobal(piece.position);
currentSquare.removeChild(piece);
game.addChild(piece);
piece.x = currentGlobalPos.x;
piece.y = currentGlobalPos.y;
// Remove piece from old position on board
board[fromRow][fromCol].setPiece(null);
// Handle captured piece animation if there's a capture
if (capturedPiece) {
// Create explosion effect at capture location
var explosionX = BOARD_START_X + toCol * SQUARE_SIZE + SQUARE_SIZE / 2;
var explosionY = BOARD_START_Y + toRow * SQUARE_SIZE + SQUARE_SIZE / 2;
createExplosionEffect(explosionX, explosionY);
// Add to captured pieces array
if (capturedPiece.pieceColor === 'white') {
capturedWhitePieces.push({
pieceType: capturedPiece.pieceType,
pieceColor: capturedPiece.pieceColor
});
} else {
capturedBlackPieces.push({
pieceType: capturedPiece.pieceType,
pieceColor: capturedPiece.pieceColor
});
}
// Remove captured piece from board immediately
board[toRow][toCol].setPiece(null);
// Animate captured piece moving to captured area
var capturedTargetX = capturedPiecesContainer.x;
var capturedTargetY = capturedPiecesContainer.y + 100;
// First animate to captured area, then scale down
tween(capturedPiece, {
x: capturedTargetX,
y: capturedTargetY,
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then fade out and destroy
tween(capturedPiece, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
// Destroy the captured piece and update display
capturedPiece.destroy();
updateCapturedPiecesDisplay();
}
});
}
});
}
// Set piece in new position but don't add to square yet
board[toRow][toCol].piece = piece;
piece.updateBoardPosition(toRow, toCol);
// Animate piece to target position
tween(piece, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Move piece from game to target square
game.removeChild(piece);
board[toRow][toCol].setPiece(piece);
// Play sound after animation
if (capturedPiece) {
LK.getSound('captureSound').play();
} else {
LK.getSound('moveSound').play();
}
// Check for pawn promotion
if (piece.pieceType === 'pawn') {
if (piece.pieceColor === 'white' && toRow === 0 || piece.pieceColor === 'black' && toRow === 7) {
// Promote to queen
board[toRow][toCol].setPiece(null);
var promotedQueen = new ChessPiece('queen', piece.pieceColor, toRow, toCol);
board[toRow][toCol].setPiece(promotedQueen);
}
}
// Switch turns
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
// Remove previous king highlight if king is no longer in check
if (checkedKing && !isInCheck(checkedKing.pieceColor)) {
removeKingHighlight();
}
// Reset AI move flag when switching turns
if (currentPlayer === 'black') {
aiHasMoved = false;
}
// Reset dragged piece to null to re-enable piece interaction
draggedPiece = null;
// Check for game end conditions first
if (isCheckmate(currentPlayer)) {
// Highlight the checkmated king
highlightCheckedKing(currentPlayer);
// Play checkmate sound
LK.getSound('checkmateSound').play();
// Create rainbow checkmate text
var checkmateText = new Text2('Check Mate!', {
size: 150,
fill: 0xff0000 // Start with red
});
checkmateText.anchor.set(0.5, 0.5);
checkmateText.x = 2048 / 2;
checkmateText.y = 300;
game.addChild(checkmateText);
// Rainbow color animation - cycle through colors
var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff];
var colorIndex = 0;
var colorTimer = LK.setInterval(function () {
colorIndex = (colorIndex + 1) % colors.length;
checkmateText.tint = colors[colorIndex];
}, 100);
// Fade out the text slowly
tween(checkmateText, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.clearInterval(colorTimer);
game.removeChild(checkmateText);
}
});
// End game 1 second after checkmate text appears
LK.setTimeout(function () {
if (currentPlayer === 'white') {
LK.showGameOver();
} else {
LK.showYouWin();
}
gameState = 'gameOver';
}, 1000);
} else if (isStalemate(currentPlayer)) {
LK.showGameOver(); // Show as draw
gameState = 'gameOver';
} else if (isInCheck(currentPlayer)) {
// Highlight the checked king
highlightCheckedKing(currentPlayer);
// Play check sound
LK.getSound('checkSound').play();
// Create rainbow check text
var checkText = new Text2('Check!', {
size: 120,
fill: 0xff0000 // Start with red
});
checkText.anchor.set(0.5, 0.5);
checkText.x = 2048 / 2;
checkText.y = 300;
game.addChild(checkText);
// Rainbow color animation - cycle through colors
var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff];
var colorIndex = 0;
var colorTimer = LK.setInterval(function () {
colorIndex = (colorIndex + 1) % colors.length;
checkText.tint = colors[colorIndex];
}, 100);
// Fade out the text slowly
tween(checkText, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.clearInterval(colorTimer);
game.removeChild(checkText);
}
});
}
}
});
} else {
// For player moves, animate the piece smoothly to target position
// Remove piece from old position
board[fromRow][fromCol].setPiece(null);
// Set piece in new position but don't add to square yet
board[toRow][toCol].piece = piece;
piece.updateBoardPosition(toRow, toCol);
// Add capture animation if there's a captured piece
if (capturedPiece) {
// Create explosion effect at capture location
createExplosionEffect(targetX, targetY);
// Add to captured pieces array
if (capturedPiece.pieceColor === 'white') {
capturedWhitePieces.push({
pieceType: capturedPiece.pieceType,
pieceColor: capturedPiece.pieceColor
});
} else {
capturedBlackPieces.push({
pieceType: capturedPiece.pieceType,
pieceColor: capturedPiece.pieceColor
});
}
// Animate captured piece moving to captured area first
var capturedTargetX = capturedPiecesContainer.x;
var capturedTargetY = capturedPiecesContainer.y + 100;
tween(capturedPiece, {
x: capturedTargetX,
y: capturedTargetY,
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then scale down the captured piece before removing
tween(capturedPiece, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
// Captured piece animation complete
updateCapturedPiecesDisplay();
}
});
}
});
}
// Animate piece to target position
tween(piece, {
x: targetX,
y: targetY
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
// Move piece from game to target square
game.removeChild(piece);
board[toRow][toCol].setPiece(piece);
// Play sound after animation
if (capturedPiece) {
LK.getSound('captureSound').play();
} else {
LK.getSound('moveSound').play();
}
// Check for pawn promotion
if (piece.pieceType === 'pawn') {
if (piece.pieceColor === 'white' && toRow === 0 || piece.pieceColor === 'black' && toRow === 7) {
// Promote to queen
board[toRow][toCol].setPiece(null);
var promotedQueen = new ChessPiece('queen', piece.pieceColor, toRow, toCol);
board[toRow][toCol].setPiece(promotedQueen);
}
}
// Switch turns
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
// Remove previous king highlight if king is no longer in check
if (checkedKing && !isInCheck(checkedKing.pieceColor)) {
removeKingHighlight();
}
// Reset AI move flag when switching turns
if (currentPlayer === 'black') {
aiHasMoved = false;
}
// Reset dragged piece to null to re-enable piece interaction
draggedPiece = null;
// Check for game end conditions first
if (isCheckmate(currentPlayer)) {
// Highlight the checkmated king
highlightCheckedKing(currentPlayer);
// Play checkmate sound
LK.getSound('checkmateSound').play();
// Create rainbow checkmate text
var checkmateText = new Text2('Check Mate!', {
size: 150,
fill: 0xff0000 // Start with red
});
checkmateText.anchor.set(0.5, 0.5);
checkmateText.x = 2048 / 2;
checkmateText.y = 300;
game.addChild(checkmateText);
// Rainbow color animation - cycle through colors
var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff];
var colorIndex = 0;
var colorTimer = LK.setInterval(function () {
colorIndex = (colorIndex + 1) % colors.length;
checkmateText.tint = colors[colorIndex];
}, 100);
// Fade out the text slowly
tween(checkmateText, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.clearInterval(colorTimer);
game.removeChild(checkmateText);
}
});
// End game 1 second after checkmate text appears
LK.setTimeout(function () {
if (currentPlayer === 'white') {
LK.showGameOver();
} else {
LK.showYouWin();
}
gameState = 'gameOver';
}, 1000);
} else if (isStalemate(currentPlayer)) {
LK.showGameOver(); // Show as draw
gameState = 'gameOver';
} else if (isInCheck(currentPlayer)) {
// Highlight the checked king
highlightCheckedKing(currentPlayer);
// Play check sound
LK.getSound('checkSound').play();
// Create rainbow check text
var checkText = new Text2('Check!', {
size: 120,
fill: 0xff0000 // Start with red
});
checkText.anchor.set(0.5, 0.5);
checkText.x = 2048 / 2;
checkText.y = 300;
game.addChild(checkText);
// Rainbow color animation - cycle through colors
var colors = [0xff0000, 0xff8800, 0xffff00, 0x00ff00, 0x0088ff, 0x0000ff, 0x8800ff];
var colorIndex = 0;
var colorTimer = LK.setInterval(function () {
colorIndex = (colorIndex + 1) % colors.length;
checkText.tint = colors[colorIndex];
}, 100);
// Fade out the text slowly
tween(checkText, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.clearInterval(colorTimer);
game.removeChild(checkText);
}
});
}
}
});
}
}
// Check if a king is in check
function isInCheck(color) {
// Find king position
var kingPos = null;
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceType === 'king' && piece.pieceColor === color) {
kingPos = {
row: row,
col: col
};
break;
}
}
if (kingPos) break;
}
if (!kingPos) return false; // No king found
// Check if any enemy piece can attack the king
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceColor !== color) {
var moves = getRawMoves(piece, row, col);
for (var i = 0; i < moves.length; i++) {
if (moves[i].row === kingPos.row && moves[i].col === kingPos.col) {
return true; // King is under attack
}
}
}
}
}
return false; // King is safe
}
// Simple checkmate detection - king must be in check AND have no legal moves
function isCheckmate(color) {
// First check if king is in check
if (!isInCheck(color)) {
return false; // Not checkmate if king is not in check
}
// Find king
var kingPos = null;
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceType === 'king' && piece.pieceColor === color) {
kingPos = {
row: row,
col: col
};
break;
}
}
if (kingPos) break;
}
if (!kingPos) return true; // No king found
// Check if any piece can make a legal move
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceColor === color) {
var moves = getLegalMoves(piece, row, col);
if (moves.length > 0) {
return false; // Found a legal move
}
}
}
}
return true; // No legal moves found and king is in check
}
// Simple stalemate detection - king is NOT in check but has no legal moves
function isStalemate(color) {
// First check if king is in check
if (isInCheck(color)) {
return false; // Not stalemate if king is in check
}
// Check if any piece can make a legal move
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceColor === color) {
var moves = getLegalMoves(piece, row, col);
if (moves.length > 0) {
return false; // Found a legal move
}
}
}
}
return true; // No legal moves found but king is not in check
}
// AI move selection based on difficulty
function getAIMove() {
var allMoves = [];
// Get all possible moves for AI (black pieces)
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var piece = board[row][col].piece;
if (piece && piece.pieceColor === 'black') {
var moves = getLegalMoves(piece, row, col);
for (var m = 0; m < moves.length; m++) {
// Prevent AI from selecting a move that would capture the king
var target = board[moves[m].row][moves[m].col].piece;
if (target && target.pieceType === 'king') {
continue;
}
allMoves.push({
fromRow: row,
fromCol: col,
toRow: moves[m].row,
toCol: moves[m].col,
piece: piece
});
}
}
}
}
if (allMoves.length === 0) return null;
if (selectedDifficulty === 'easy') {
// Random move
return allMoves[Math.floor(Math.random() * allMoves.length)];
} else if (selectedDifficulty === 'medium') {
// Prefer captures
var captures = [];
for (var i = 0; i < allMoves.length; i++) {
var move = allMoves[i];
if (board[move.toRow][move.toCol].piece) {
captures.push(move);
}
}
if (captures.length > 0) {
return captures[Math.floor(Math.random() * captures.length)];
}
return allMoves[Math.floor(Math.random() * allMoves.length)];
} else {
// Hard: prefer captures, then center moves
var captures = [];
var centerMoves = [];
for (var i = 0; i < allMoves.length; i++) {
var move = allMoves[i];
if (board[move.toRow][move.toCol].piece) {
captures.push(move);
} else if (move.toRow >= 3 && move.toRow <= 4 && move.toCol >= 3 && move.toCol <= 4) {
centerMoves.push(move);
}
}
if (captures.length > 0) {
return captures[Math.floor(Math.random() * captures.length)];
}
if (centerMoves.length > 0) {
return centerMoves[Math.floor(Math.random() * centerMoves.length)];
}
return allMoves[Math.floor(Math.random() * allMoves.length)];
}
}
// Get board position from screen coordinates
function getBoardPosition(x, y) {
var col = Math.floor((x - BOARD_START_X) / SQUARE_SIZE);
var row = Math.floor((y - BOARD_START_Y) / SQUARE_SIZE);
if (row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE) {
return {
row: row,
col: col
};
}
return null;
}
// Handle mouse/touch down
game.down = function (x, y, obj) {
if (gameState !== 'playing' || currentPlayer !== 'white') return;
var pos = getBoardPosition(x, y);
if (!pos) return;
var square = board[pos.row][pos.col];
var piece = square.piece;
if (piece && piece.pieceColor === 'white') {
// Start dragging
draggedPiece = piece;
// Set drag offset to zero so piece stays at click position
dragOffset.x = 0;
dragOffset.y = 0;
// Clear previous highlights
clearHighlights();
// Highlight possible moves
possibleMoves = getLegalMoves(piece, pos.row, pos.col);
highlightMoves(possibleMoves);
selectedSquare = {
row: pos.row,
col: pos.col
};
// Bring piece to front but keep it at current position
var tempParent = piece.parent;
var currentGlobalPos = tempParent.toGlobal(piece.position);
tempParent.removeChild(piece);
game.addChild(piece);
piece.x = currentGlobalPos.x;
piece.y = currentGlobalPos.y;
// Add selection animation
tween(piece, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 150,
easing: tween.easeOut
});
}
};
// Handle mouse/touch move
game.move = function (x, y, obj) {
if (draggedPiece) {
draggedPiece.x = x;
draggedPiece.y = y;
}
};
// Handle mouse/touch up
game.up = function (x, y, obj) {
if (!draggedPiece || gameState !== 'playing') return;
var pos = getBoardPosition(x, y);
var validMove = false;
if (pos && selectedSquare) {
// Check if this is a valid move
for (var i = 0; i < possibleMoves.length; i++) {
if (possibleMoves[i].row === pos.row && possibleMoves[i].col === pos.col) {
validMove = true;
break;
}
}
if (validMove) {
// Prevent player from capturing the king
var targetPiece = board[pos.row][pos.col].piece;
if (targetPiece && targetPiece.pieceType === 'king') {
// Invalid move: cannot capture king, revert and return
game.removeChild(draggedPiece);
board[selectedSquare.row][selectedSquare.col].setPiece(draggedPiece);
clearHighlights();
draggedPiece = null;
selectedSquare = null;
possibleMoves = [];
return;
}
// Make the move
makeMove(selectedSquare.row, selectedSquare.col, pos.row, pos.col);
// Clear selection state after valid move
selectedSquare = null;
possibleMoves = [];
}
}
if (!validMove && selectedSquare) {
// Return piece to original position with animation
var originalSquare = board[selectedSquare.row][selectedSquare.col];
var originalX = BOARD_START_X + selectedSquare.col * SQUARE_SIZE + SQUARE_SIZE / 2;
var originalY = BOARD_START_Y + selectedSquare.row * SQUARE_SIZE + SQUARE_SIZE / 2;
// Reset scale and animate back to original position
tween(draggedPiece, {
x: originalX,
y: originalY,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(draggedPiece);
originalSquare.setPiece(draggedPiece);
// Reset dragging state after animation completes
draggedPiece = null;
selectedSquare = null;
possibleMoves = [];
}
});
// Clear highlights immediately
clearHighlights();
} else {
// Reset scale for valid moves
if (draggedPiece) {
tween(draggedPiece, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
}
// Clean up for valid moves
clearHighlights();
draggedPiece = null;
selectedSquare = null;
possibleMoves = [];
}
};
// Game update loop
game.update = function () {
if (gameState === 'playing' && currentPlayer === 'black' && !draggedPiece && !aiHasMoved) {
// AI turn - only if player is not currently dragging a piece and AI hasn't moved yet
if (LK.ticks % 60 === 0) {
// Wait 1 second before AI move
aiHasMoved = true; // Set flag immediately to prevent multiple moves
LK.setTimeout(function () {
var aiMove = getAIMove();
if (aiMove) {
makeMove(aiMove.fromRow, aiMove.fromCol, aiMove.toRow, aiMove.toCol);
}
}, 1000);
}
}
};
// Play background music
LK.playMusic('Game_music');
// Initialize the game
if (gameState === 'difficulty') {
initializeDifficultySelection();
}
Chess pawn View from above. In-Game asset. 2d. High contrast. No shadows
Chess king view from above. In-Game asset. 2d. High contrast. No shadows
Chess White king view from top. In-Game asset. 2d. High contrast. No shadows
Chess queen white View from above. In-Game asset. 2d. High contrast. No shadows
Chess black bishop. In-Game asset. 2d. High contrast. No shadows
Chess black queen. In-Game asset. 2d. High contrast. No shadows
Chess black knight. In-Game asset. 2d. High contrast. No shadows
Chess rook view from above. In-Game asset. 2d. High contrast. No shadows
Chess bishop white view frome above. In-Game asset. 2d. High contrast. No shadows
Şimdi içini beyaz renkle doldur