User prompt
make the chess bot smarter
User prompt
add coordinates of the squares
User prompt
add a button at the right bottom to restart the game
User prompt
Please fix the bug: 'self.getPieceSymbol is not a function' in or related to this line: 'self.symbol = new Text2(self.getPieceSymbol(), {' Line Number: 109
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'fill')' in or related to this line: 'easyBtn.style.fill = "#ffff00";' Line Number: 1276
Code edit (1 edits merged)
Please save this source code
User prompt
Classic Chess Challenge
Initial prompt
chess game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { aiDifficulty: 1, gameHistory: [] }); /**** * Classes ****/ var Board = Container.expand(function () { var self = Container.call(this); self.tiles = []; self.tileSize = 256; self.boardSize = 8 * self.tileSize; self.highlightLayer = new Container(); self.piecesLayer = new Container(); self.createBoard = function () { // Create tiles for (var row = 0; row < 8; row++) { self.tiles[row] = []; for (var col = 0; col < 8; col++) { var isWhite = (row + col) % 2 === 0; var tile = self.attachAsset(isWhite ? 'whiteTile' : 'blackTile', { x: col * self.tileSize, y: row * self.tileSize, anchorX: 0, anchorY: 0 }); self.tiles[row][col] = tile; tile.boardRow = row; tile.boardCol = col; // Add coordinate labels if (row === 7) { // Add file (column) labels at the bottom var fileLabel = new Text2(String.fromCharCode(97 + col), { size: 30, fill: isWhite ? "#333333" : "#ffffff" }); fileLabel.anchor.set(1.0, 1.0); fileLabel.x = (col + 1) * self.tileSize - 10; fileLabel.y = (row + 1) * self.tileSize - 10; self.addChild(fileLabel); } if (col === 0) { // Add rank (row) labels on the left var rankLabel = new Text2(String(8 - row), { size: 30, fill: isWhite ? "#333333" : "#ffffff" }); rankLabel.anchor.set(0.0, 0.0); rankLabel.x = col * self.tileSize + 10; rankLabel.y = row * self.tileSize + 10; self.addChild(rankLabel); } } } self.addChild(self.highlightLayer); self.addChild(self.piecesLayer); }; self.highlightTile = function (row, col, type) { var highlight = LK.getAsset(type, { x: col * self.tileSize, y: row * self.tileSize, anchorX: 0, anchorY: 0, alpha: 0.5 }); self.highlightLayer.addChild(highlight); return highlight; }; self.clearHighlights = function () { while (self.highlightLayer.children.length > 0) { self.highlightLayer.removeChildAt(0); } }; self.getTileAt = function (x, y) { var col = Math.floor(x / self.tileSize); var row = Math.floor(y / self.tileSize); if (row >= 0 && row < 8 && col >= 0 && col < 8) { return { row: row, col: col }; } return null; }; return self; }); var Piece = Container.expand(function (type, color, row, col) { var self = Container.call(this); self.type = type; self.color = color; self.row = row; self.col = col; self.hasMoved = false; self.getPieceSymbol = function () { switch (self.type) { case 'king': return '♚'; case 'queen': return '♛'; case 'rook': return '♜'; case 'bishop': return '♝'; case 'knight': return '♞'; case 'pawn': return '♟'; default: return ''; } }; // Get asset id based on color and type var assetId = color + type.charAt(0).toUpperCase() + type.slice(1); self.sprite = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add chess piece symbol self.symbol = new Text2(self.getPieceSymbol(), { size: 150, fill: color === 'white' ? "#333333" : "#ffffff" }); self.symbol.anchor.set(0.5, 0.5); self.addChild(self.symbol); self.moveTo = function (row, col, duration) { self.row = row; self.col = col; var newX = col * chessBoard.tileSize + chessBoard.tileSize / 2; var newY = row * chessBoard.tileSize + chessBoard.tileSize / 2; if (duration) { tween(self, { x: newX, y: newY }, { duration: duration, easing: tween.easeOutQuad }); } else { self.x = newX; self.y = newY; } self.hasMoved = true; }; self.update = function () { // For any per-frame updates }; self.down = function (x, y, obj) { if (gameState.currentTurn === self.color && !gameState.isAnimating) { gameState.selectedPiece = self; chessBoard.clearHighlights(); // Highlight the current piece's tile self.currentHighlight = chessBoard.highlightTile(self.row, self.col, 'highlightTile'); // Highlight valid moves var validMoves = getValidMoves(self); gameState.validMoves = validMoves; for (var i = 0; i < validMoves.length; i++) { var move = validMoves[i]; chessBoard.highlightTile(move.row, move.col, 'validMoveTile'); } } }; // Place piece at its position self.x = col * 256 + 128; self.y = row * 256 + 128; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ // Game state var gameState = { currentTurn: 'white', selectedPiece: null, validMoves: [], board: Array(8).fill().map(function () { return Array(8).fill(null); }), isAnimating: false, lastMove: null, aiDifficulty: storage.aiDifficulty || 1, gameMode: 'ai', // 'ai' or 'twoPlayer' status: 'playing', // 'playing', 'check', 'checkmate', 'stalemate' aiPlayer: 'black' }; // Create UI var titleText = new Text2('Chess Challenge', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); LK.gui.top.addChild(titleText); var turnText = new Text2('White to move', { size: 50, fill: 0xFFFFFF }); turnText.anchor.set(0.5, 0); turnText.y = 100; LK.gui.top.addChild(turnText); var statusText = new Text2('', { size: 50, fill: 0xFFFF00 }); statusText.anchor.set(0.5, 0); statusText.y = 160; LK.gui.top.addChild(statusText); // Create and position the chess board var chessBoard = new Board(); game.addChild(chessBoard); chessBoard.createBoard(); // Center the board chessBoard.x = (2048 - chessBoard.boardSize) / 2; chessBoard.y = (2732 - chessBoard.boardSize) / 2; // Initialize pieces function initializeChessBoard() { // Clear existing pieces while (chessBoard.piecesLayer.children.length > 0) { chessBoard.piecesLayer.removeChildAt(0); } // Reset game state gameState.board = Array(8).fill().map(function () { return Array(8).fill(null); }); gameState.currentTurn = 'white'; gameState.selectedPiece = null; gameState.validMoves = []; gameState.isAnimating = false; gameState.lastMove = null; gameState.status = 'playing'; // Update UI updateTurnText(); // Create pieces var pieces = [{ type: 'rook', color: 'black', row: 0, col: 0 }, { type: 'knight', color: 'black', row: 0, col: 1 }, { type: 'bishop', color: 'black', row: 0, col: 2 }, { type: 'queen', color: 'black', row: 0, col: 3 }, { type: 'king', color: 'black', row: 0, col: 4 }, { type: 'bishop', color: 'black', row: 0, col: 5 }, { type: 'knight', color: 'black', row: 0, col: 6 }, { type: 'rook', color: 'black', row: 0, col: 7 }, { type: 'rook', color: 'white', row: 7, col: 0 }, { type: 'knight', color: 'white', row: 7, col: 1 }, { type: 'bishop', color: 'white', row: 7, col: 2 }, { type: 'queen', color: 'white', row: 7, col: 3 }, { type: 'king', color: 'white', row: 7, col: 4 }, { type: 'bishop', color: 'white', row: 7, col: 5 }, { type: 'knight', color: 'white', row: 7, col: 6 }, { type: 'rook', color: 'white', row: 7, col: 7 }]; // Add pawns for (var i = 0; i < 8; i++) { pieces.push({ type: 'pawn', color: 'black', row: 1, col: i }); pieces.push({ type: 'pawn', color: 'white', row: 6, col: i }); } // Create and position pieces for (var i = 0; i < pieces.length; i++) { var p = pieces[i]; var piece = new Piece(p.type, p.color, p.row, p.col); chessBoard.piecesLayer.addChild(piece); gameState.board[p.row][p.col] = piece; } // Clear highlights chessBoard.clearHighlights(); } // Chess move validation function getValidMoves(piece) { var moves = []; if (!piece) return moves; function addMove(row, col) { moves.push({ row: row, col: col }); } function isOnBoard(row, col) { return row >= 0 && row < 8 && col >= 0 && col < 8; } function isOccupied(row, col) { return gameState.board[row][col] !== null; } function isEnemyPiece(row, col) { return isOccupied(row, col) && gameState.board[row][col].color !== piece.color; } function isFriendlyPiece(row, col) { return isOccupied(row, col) && gameState.board[row][col].color === piece.color; } var row = piece.row; var col = piece.col; switch (piece.type) { case 'pawn': var direction = piece.color === 'white' ? -1 : 1; // Move forward one square if (isOnBoard(row + direction, col) && !isOccupied(row + direction, col)) { addMove(row + direction, col); // Move forward two squares on first move if (!piece.hasMoved && isOnBoard(row + 2 * direction, col) && !isOccupied(row + 2 * direction, col)) { addMove(row + 2 * direction, col); } } // Capture diagonally if (isOnBoard(row + direction, col - 1) && isEnemyPiece(row + direction, col - 1)) { addMove(row + direction, col - 1); } if (isOnBoard(row + direction, col + 1) && isEnemyPiece(row + direction, col + 1)) { addMove(row + direction, col + 1); } // En passant (simplified) if (gameState.lastMove && gameState.lastMove.piece.type === 'pawn' && gameState.lastMove.fromRow === (piece.color === 'white' ? 1 : 6) && gameState.lastMove.toRow === (piece.color === 'white' ? 3 : 4) && Math.abs(gameState.lastMove.toCol - col) === 1 && row === gameState.lastMove.toRow) { addMove(row + direction, gameState.lastMove.toCol); } break; case 'rook': // Horizontal and vertical directions var directions = [{ dr: -1, dc: 0 }, // up { dr: 1, dc: 0 }, // down { dr: 0, dc: -1 }, // left { dr: 0, dc: 1 } // right ]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; while (isOnBoard(r, c)) { if (isOccupied(r, c)) { if (isEnemyPiece(r, c)) { addMove(r, c); } break; } addMove(r, c); r += dir.dr; c += dir.dc; } }); break; case 'knight': // Knight moves var knightMoves = [{ dr: -2, dc: -1 }, { dr: -2, dc: 1 }, { dr: -1, dc: -2 }, { dr: -1, dc: 2 }, { dr: 1, dc: -2 }, { dr: 1, dc: 2 }, { dr: 2, dc: -1 }, { dr: 2, dc: 1 }]; knightMoves.forEach(function (move) { var r = row + move.dr; var c = col + move.dc; if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) { addMove(r, c); } }); break; case 'bishop': // Diagonal directions var directions = [{ dr: -1, dc: -1 }, // up-left { dr: -1, dc: 1 }, // up-right { dr: 1, dc: -1 }, // down-left { dr: 1, dc: 1 } // down-right ]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; while (isOnBoard(r, c)) { if (isOccupied(r, c)) { if (isEnemyPiece(r, c)) { addMove(r, c); } break; } addMove(r, c); r += dir.dr; c += dir.dc; } }); break; case 'queen': // Combine rook and bishop moves var directions = [{ dr: -1, dc: 0 }, { dr: 1, dc: 0 }, // vertical { dr: 0, dc: -1 }, { dr: 0, dc: 1 }, // horizontal { dr: -1, dc: -1 }, { dr: -1, dc: 1 }, // diagonal { dr: 1, dc: -1 }, { dr: 1, dc: 1 } // diagonal ]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; while (isOnBoard(r, c)) { if (isOccupied(r, c)) { if (isEnemyPiece(r, c)) { addMove(r, c); } break; } addMove(r, c); r += dir.dr; c += dir.dc; } }); break; case 'king': // King moves (one square in any direction) var directions = [{ dr: -1, dc: -1 }, { dr: -1, dc: 0 }, { dr: -1, dc: 1 }, { dr: 0, dc: -1 }, { dr: 0, dc: 1 }, { dr: 1, dc: -1 }, { dr: 1, dc: 0 }, { dr: 1, dc: 1 }]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) { addMove(r, c); } }); // Castling if (!piece.hasMoved) { // Kingside castling if (!isOccupied(row, col + 1) && !isOccupied(row, col + 2) && isOccupied(row, col + 3) && !gameState.board[row][col + 3].hasMoved && gameState.board[row][col + 3].type === 'rook') { addMove(row, col + 2); } // Queenside castling if (!isOccupied(row, col - 1) && !isOccupied(row, col - 2) && !isOccupied(row, col - 3) && isOccupied(row, col - 4) && !gameState.board[row][col - 4].hasMoved && gameState.board[row][col - 4].type === 'rook') { addMove(row, col - 2); } } break; } // Filter out moves that would put the king in check var validMoves = []; for (var i = 0; i < moves.length; i++) { var move = moves[i]; // Make a temporary move var tempPiece = gameState.board[move.row][move.col]; gameState.board[move.row][move.col] = piece; gameState.board[row][col] = null; // Check if the move puts/leaves the king in check var inCheck = isKingInCheck(piece.color); // Undo the move gameState.board[row][col] = piece; gameState.board[move.row][move.col] = tempPiece; if (!inCheck) { validMoves.push(move); } } return validMoves; } function isKingInCheck(color) { // Find the king var kingRow = -1; var kingCol = -1; for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (piece && piece.type === 'king' && piece.color === color) { kingRow = r; kingCol = c; break; } } if (kingRow !== -1) break; } // Check if any enemy piece can capture the king for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (piece && piece.color !== color) { // Get the enemy's valid moves (ignoring check) var moves = getBasicMoves(piece); // See if any of the moves can capture the king for (var i = 0; i < moves.length; i++) { if (moves[i].row === kingRow && moves[i].col === kingCol) { return true; } } } } } return false; } function getBasicMoves(piece) { var moves = []; if (!piece) return moves; function addMove(row, col) { moves.push({ row: row, col: col }); } function isOnBoard(row, col) { return row >= 0 && row < 8 && col >= 0 && col < 8; } function isOccupied(row, col) { return gameState.board[row][col] !== null; } function isEnemyPiece(row, col) { return isOccupied(row, col) && gameState.board[row][col].color !== piece.color; } function isFriendlyPiece(row, col) { return isOccupied(row, col) && gameState.board[row][col].color === piece.color; } var row = piece.row; var col = piece.col; switch (piece.type) { case 'pawn': var direction = piece.color === 'white' ? -1 : 1; // Move forward one square if (isOnBoard(row + direction, col) && !isOccupied(row + direction, col)) { addMove(row + direction, col); // Move forward two squares on first move if (!piece.hasMoved && isOnBoard(row + 2 * direction, col) && !isOccupied(row + 2 * direction, col)) { addMove(row + 2 * direction, col); } } // Capture diagonally if (isOnBoard(row + direction, col - 1) && isEnemyPiece(row + direction, col - 1)) { addMove(row + direction, col - 1); } if (isOnBoard(row + direction, col + 1) && isEnemyPiece(row + direction, col + 1)) { addMove(row + direction, col + 1); } break; case 'rook': // Horizontal and vertical directions var directions = [{ dr: -1, dc: 0 }, // up { dr: 1, dc: 0 }, // down { dr: 0, dc: -1 }, // left { dr: 0, dc: 1 } // right ]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; while (isOnBoard(r, c)) { if (isOccupied(r, c)) { if (isEnemyPiece(r, c)) { addMove(r, c); } break; } addMove(r, c); r += dir.dr; c += dir.dc; } }); break; case 'knight': // Knight moves var knightMoves = [{ dr: -2, dc: -1 }, { dr: -2, dc: 1 }, { dr: -1, dc: -2 }, { dr: -1, dc: 2 }, { dr: 1, dc: -2 }, { dr: 1, dc: 2 }, { dr: 2, dc: -1 }, { dr: 2, dc: 1 }]; knightMoves.forEach(function (move) { var r = row + move.dr; var c = col + move.dc; if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) { addMove(r, c); } }); break; case 'bishop': // Diagonal directions var directions = [{ dr: -1, dc: -1 }, // up-left { dr: -1, dc: 1 }, // up-right { dr: 1, dc: -1 }, // down-left { dr: 1, dc: 1 } // down-right ]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; while (isOnBoard(r, c)) { if (isOccupied(r, c)) { if (isEnemyPiece(r, c)) { addMove(r, c); } break; } addMove(r, c); r += dir.dr; c += dir.dc; } }); break; case 'queen': // Combine rook and bishop moves var directions = [{ dr: -1, dc: 0 }, { dr: 1, dc: 0 }, // vertical { dr: 0, dc: -1 }, { dr: 0, dc: 1 }, // horizontal { dr: -1, dc: -1 }, { dr: -1, dc: 1 }, // diagonal { dr: 1, dc: -1 }, { dr: 1, dc: 1 } // diagonal ]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; while (isOnBoard(r, c)) { if (isOccupied(r, c)) { if (isEnemyPiece(r, c)) { addMove(r, c); } break; } addMove(r, c); r += dir.dr; c += dir.dc; } }); break; case 'king': // King moves (one square in any direction) var directions = [{ dr: -1, dc: -1 }, { dr: -1, dc: 0 }, { dr: -1, dc: 1 }, { dr: 0, dc: -1 }, { dr: 0, dc: 1 }, { dr: 1, dc: -1 }, { dr: 1, dc: 0 }, { dr: 1, dc: 1 }]; directions.forEach(function (dir) { var r = row + dir.dr; var c = col + dir.dc; if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) { addMove(r, c); } }); break; } return moves; } function isCheckmate(color) { if (!isKingInCheck(color)) return false; // Check if any piece can make a valid move for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (piece && piece.color === color) { var validMoves = getValidMoves(piece); if (validMoves.length > 0) { return false; } } } } return true; } function isStalemate(color) { if (isKingInCheck(color)) return false; // Check if any piece can make a valid move for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (piece && piece.color === color) { var validMoves = getValidMoves(piece); if (validMoves.length > 0) { return false; } } } } return true; } function updateGameStatus() { if (isCheckmate(gameState.currentTurn)) { gameState.status = 'checkmate'; var winner = gameState.currentTurn === 'white' ? 'Black' : 'White'; statusText.setText(winner + ' wins by checkmate!'); LK.getSound('gameEnd').play(); // Update score if AI was played against if (gameState.gameMode === 'ai') { if (winner === 'White' && gameState.aiPlayer === 'black' || winner === 'Black' && gameState.aiPlayer === 'white') { LK.setScore(LK.getScore() + 1); } } return true; } else if (isStalemate(gameState.currentTurn)) { gameState.status = 'stalemate'; statusText.setText('Game drawn by stalemate!'); LK.getSound('gameEnd').play(); return true; } else if (isKingInCheck(gameState.currentTurn)) { gameState.status = 'check'; statusText.setText(gameState.currentTurn.charAt(0).toUpperCase() + gameState.currentTurn.slice(1) + ' is in check!'); LK.getSound('check').play(); } else { gameState.status = 'playing'; statusText.setText(''); } return false; } function updateTurnText() { var turnName = gameState.currentTurn.charAt(0).toUpperCase() + gameState.currentTurn.slice(1); turnText.setText(turnName + ' to move'); } function movePiece(piece, targetRow, targetCol) { var isCapture = gameState.board[targetRow][targetCol] !== null; var capturedPiece = gameState.board[targetRow][targetCol]; var fromRow = piece.row; var fromCol = piece.col; // Handle castling if (piece.type === 'king' && Math.abs(targetCol - fromCol) === 2) { // Kingside castling if (targetCol > fromCol) { var rook = gameState.board[fromRow][7]; gameState.board[fromRow][5] = rook; gameState.board[fromRow][7] = null; rook.moveTo(fromRow, 5, 300); } // Queenside castling else { var rook = gameState.board[fromRow][0]; gameState.board[fromRow][3] = rook; gameState.board[fromRow][0] = null; rook.moveTo(fromRow, 3, 300); } } // Handle en passant capture if (piece.type === 'pawn' && Math.abs(targetCol - fromCol) === 1 && !isCapture) { var enPassantRow = piece.color === 'white' ? targetRow + 1 : targetRow - 1; capturedPiece = gameState.board[enPassantRow][targetCol]; gameState.board[enPassantRow][targetCol] = null; if (capturedPiece) { capturedPiece.parent.removeChild(capturedPiece); } isCapture = true; } // Update board array gameState.board[fromRow][fromCol] = null; if (capturedPiece) { capturedPiece.parent.removeChild(capturedPiece); } gameState.board[targetRow][targetCol] = piece; // Move the piece with animation gameState.isAnimating = true; piece.moveTo(targetRow, targetCol, 300); // Play sound if (isCapture) { LK.getSound('capture').play(); } else { LK.getSound('move').play(); } // Handle pawn promotion (always to queen for simplicity) if (piece.type === 'pawn' && (targetRow === 0 || targetRow === 7)) { // Wait for the piece to finish moving var promotionTimeout = LK.setTimeout(function () { // Remove the pawn piece.parent.removeChild(piece); gameState.board[targetRow][targetCol] = null; // Create a new queen var queen = new Piece('queen', piece.color, targetRow, targetCol); chessBoard.piecesLayer.addChild(queen); gameState.board[targetRow][targetCol] = queen; LK.clearTimeout(promotionTimeout); }, 350); } // Update last move gameState.lastMove = { piece: piece, fromRow: fromRow, fromCol: fromCol, toRow: targetRow, toCol: targetCol }; // Highlight the last move var moveTimeout = LK.setTimeout(function () { chessBoard.clearHighlights(); chessBoard.highlightTile(fromRow, fromCol, 'lastMoveTile'); chessBoard.highlightTile(targetRow, targetCol, 'lastMoveTile'); // Switch turns gameState.currentTurn = gameState.currentTurn === 'white' ? 'black' : 'white'; updateTurnText(); // Check for check, checkmate, stalemate var gameEnded = updateGameStatus(); gameState.isAnimating = false; gameState.selectedPiece = null; // If AI is playing and it's AI's turn if (!gameEnded && gameState.gameMode === 'ai' && gameState.currentTurn === gameState.aiPlayer) { makeAIMove(); } LK.clearTimeout(moveTimeout); }, 350); } function makeAIMove() { var aiTimeout = LK.setTimeout(function () { var allPossibleMoves = []; // Collect all possible moves for AI pieces for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (piece && piece.color === gameState.aiPlayer) { var validMoves = getValidMoves(piece); for (var i = 0; i < validMoves.length; i++) { allPossibleMoves.push({ piece: piece, move: validMoves[i] }); } } } } // Piece value tables - used to evaluate position advantages for each piece type var piecePositionValues = { pawn: [[0, 0, 0, 0, 0, 0, 0, 0], [50, 50, 50, 50, 50, 50, 50, 50], [10, 10, 20, 30, 30, 20, 10, 10], [5, 5, 10, 25, 25, 10, 5, 5], [0, 0, 0, 20, 20, 0, 0, 0], [5, -5, -10, 0, 0, -10, -5, 5], [5, 10, 10, -20, -20, 10, 10, 5], [0, 0, 0, 0, 0, 0, 0, 0]], knight: [[-50, -40, -30, -30, -30, -30, -40, -50], [-40, -20, 0, 0, 0, 0, -20, -40], [-30, 0, 10, 15, 15, 10, 0, -30], [-30, 5, 15, 20, 20, 15, 5, -30], [-30, 0, 15, 20, 20, 15, 0, -30], [-30, 5, 10, 15, 15, 10, 5, -30], [-40, -20, 0, 5, 5, 0, -20, -40], [-50, -40, -30, -30, -30, -30, -40, -50]], bishop: [[-20, -10, -10, -10, -10, -10, -10, -20], [-10, 0, 0, 0, 0, 0, 0, -10], [-10, 0, 10, 10, 10, 10, 0, -10], [-10, 5, 5, 10, 10, 5, 5, -10], [-10, 0, 5, 10, 10, 5, 0, -10], [-10, 5, 5, 5, 5, 5, 5, -10], [-10, 0, 5, 0, 0, 5, 0, -10], [-20, -10, -10, -10, -10, -10, -10, -20]], rook: [[0, 0, 0, 0, 0, 0, 0, 0], [5, 10, 10, 10, 10, 10, 10, 5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [0, 0, 0, 5, 5, 0, 0, 0]], queen: [[-20, -10, -10, -5, -5, -10, -10, -20], [-10, 0, 0, 0, 0, 0, 0, -10], [-10, 0, 5, 5, 5, 5, 0, -10], [-5, 0, 5, 5, 5, 5, 0, -5], [0, 0, 5, 5, 5, 5, 0, -5], [-10, 5, 5, 5, 5, 5, 0, -10], [-10, 0, 5, 0, 0, 0, 0, -10], [-20, -10, -10, -5, -5, -10, -10, -20]], king: [[-30, -40, -40, -50, -50, -40, -40, -30], [-30, -40, -40, -50, -50, -40, -40, -30], [-30, -40, -40, -50, -50, -40, -40, -30], [-30, -40, -40, -50, -50, -40, -40, -30], [-20, -30, -30, -40, -40, -30, -30, -20], [-10, -20, -20, -20, -20, -20, -20, -10], [20, 20, 0, 0, 0, 0, 20, 20], [20, 30, 10, 0, 0, 10, 30, 20]] }; // Base piece values var pieceValues = { pawn: 100, knight: 320, bishop: 330, rook: 500, queen: 900, king: 20000 }; if (allPossibleMoves.length > 0) { var selectedMove; // Basic AI difficulty levels if (gameState.aiDifficulty === 1) { // Easy: Random move with slight preference for captures var captureMoves = allPossibleMoves.filter(function (moveObj) { return gameState.board[moveObj.move.row][moveObj.move.col] !== null; }); if (captureMoves.length > 0 && Math.random() > 0.5) { selectedMove = captureMoves[Math.floor(Math.random() * captureMoves.length)]; } else { selectedMove = allPossibleMoves[Math.floor(Math.random() * allPossibleMoves.length)]; } } else if (gameState.aiDifficulty === 2) { // Medium: Prefer captures and positional advantages var moveScores = []; for (var i = 0; i < allPossibleMoves.length; i++) { var moveObj = allPossibleMoves[i]; var targetPiece = gameState.board[moveObj.move.row][moveObj.move.col]; var score = 0; // Capture value if (targetPiece) { score += pieceValues[targetPiece.type]; } // Position value (flipped for black pieces) var row = moveObj.move.row; var col = moveObj.move.col; if (gameState.aiPlayer === 'black') { row = 7 - row; } score += piecePositionValues[moveObj.piece.type][row][col] * 0.5; moveScores.push({ move: moveObj, score: score }); } // Sort moves by score moveScores.sort(function (a, b) { return b.score - a.score; }); // Pick from top 3 moves to add some randomness var topMoves = moveScores.slice(0, Math.min(3, moveScores.length)); selectedMove = topMoves[Math.floor(Math.random() * topMoves.length)].move; } else { // Hard: Minimax with alpha-beta pruning (simplified) var bestScore = -Infinity; var bestMoves = []; for (var i = 0; i < allPossibleMoves.length; i++) { var moveObj = allPossibleMoves[i]; var piece = moveObj.piece; var targetRow = moveObj.move.row; var targetCol = moveObj.move.col; var originalRow = piece.row; var originalCol = piece.col; var capturedPiece = gameState.board[targetRow][targetCol]; // Make temporary move gameState.board[targetRow][targetCol] = piece; gameState.board[originalRow][originalCol] = null; // Evaluate board after move var score = evaluateBoard(gameState.aiPlayer); // Unmake the move gameState.board[originalRow][originalCol] = piece; gameState.board[targetRow][targetCol] = capturedPiece; if (score > bestScore) { bestScore = score; bestMoves = [moveObj]; } else if (score === bestScore) { bestMoves.push(moveObj); } } if (bestMoves.length > 0) { selectedMove = bestMoves[Math.floor(Math.random() * bestMoves.length)]; } else { selectedMove = allPossibleMoves[Math.floor(Math.random() * allPossibleMoves.length)]; } } // Make the selected move movePiece(selectedMove.piece, selectedMove.move.row, selectedMove.move.col); } LK.clearTimeout(aiTimeout); }, 500); // Function to evaluate the current board position function evaluateBoard(playerColor) { var score = 0; var opponentColor = playerColor === 'white' ? 'black' : 'white'; // Count material and positional advantages for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (!piece) continue; var value = pieceValues[piece.type]; // Add position value var row = r; var col = c; if (piece.color === 'black') { row = 7 - r; // Flip board for black } var positionValue = piecePositionValues[piece.type][row][col]; // Add to score if our piece, subtract if opponent's if (piece.color === playerColor) { score += value + positionValue; // Bonus for attacking opponent pieces var attackedSquares = getAttackedSquares(piece); for (var i = 0; i < attackedSquares.length; i++) { var attackedPiece = gameState.board[attackedSquares[i].row][attackedSquares[i].col]; if (attackedPiece && attackedPiece.color === opponentColor) { score += value * 0.1; // Small bonus for attacking // Higher bonus for attacking higher value pieces score += pieceValues[attackedPiece.type] * 0.05; } } // Bonus for piece mobility (number of legal moves) score += getValidMoves(piece).length * 2; // Bonus for developed pieces if (piece.type === 'knight' || piece.type === 'bishop') { var initialRow = playerColor === 'white' ? 7 : 0; if (piece.row !== initialRow) { score += 15; // Developed minor piece } } } else { score -= value + positionValue; } } } // Check if opponent is in check var tempTurn = gameState.currentTurn; gameState.currentTurn = opponentColor; if (isKingInCheck(opponentColor)) { score += 50; // Bonus for putting opponent in check // Check if it's checkmate (huge bonus) if (isCheckmate(opponentColor)) { score += 10000; } } // Penalize if our king is in check gameState.currentTurn = playerColor; if (isKingInCheck(playerColor)) { score -= 60; // Penalty for being in check } gameState.currentTurn = tempTurn; // Pawn structure evaluation score += evaluatePawnStructure(playerColor) - evaluatePawnStructure(opponentColor); // Control of center score += evaluateCenterControl(playerColor) - evaluateCenterControl(opponentColor); return score; } // Evaluate pawn structure function evaluatePawnStructure(color) { var score = 0; var pawnColumns = []; // Find all pawns for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var piece = gameState.board[r][c]; if (piece && piece.type === 'pawn' && piece.color === color) { pawnColumns.push(c); // Bonus for passed pawns (no enemy pawns ahead) var passed = true; var direction = color === 'white' ? -1 : 1; for (var checkRow = r + direction; checkRow >= 0 && checkRow < 8; checkRow += direction) { for (var checkCol = c - 1; checkCol <= c + 1; checkCol += 2) { if (checkCol >= 0 && checkCol < 8) { var checkPiece = gameState.board[checkRow][checkCol]; if (checkPiece && checkPiece.type === 'pawn' && checkPiece.color !== color) { passed = false; break; } } } if (!passed) break; } if (passed) { score += 30; // Passed pawn bonus // Bigger bonus for pawns closer to promotion var promotionDistance = color === 'white' ? r : 7 - r; score += (7 - promotionDistance) * 5; } } } } // Penalize doubled pawns (multiple pawns in same column) var columnCounts = {}; for (var i = 0; i < pawnColumns.length; i++) { var col = pawnColumns[i]; columnCounts[col] = (columnCounts[col] || 0) + 1; } for (var col in columnCounts) { if (columnCounts[col] > 1) { score -= 15 * (columnCounts[col] - 1); // Penalty for doubled pawns } } return score; } // Evaluate center control function evaluateCenterControl(color) { var score = 0; var centerSquares = [{ row: 3, col: 3 }, { row: 3, col: 4 }, { row: 4, col: 3 }, { row: 4, col: 4 }]; for (var i = 0; i < centerSquares.length; i++) { var square = centerSquares[i]; var piece = gameState.board[square.row][square.col]; // Bonus for occupying center if (piece && piece.color === color) { score += 10; } // Bonus for attacking center for (var r = 0; r < 8; r++) { for (var c = 0; c < 8; c++) { var attackingPiece = gameState.board[r][c]; if (attackingPiece && attackingPiece.color === color) { var attackedSquares = getAttackedSquares(attackingPiece); for (var j = 0; j < attackedSquares.length; j++) { if (attackedSquares[j].row === square.row && attackedSquares[j].col === square.col) { score += 5; break; } } } } } } return score; } // Get squares a piece can attack function getAttackedSquares(piece) { return getBasicMoves(piece); } } // Game event handlers game.down = function (x, y, obj) { if (gameState.isAnimating) return; // Convert screen coordinates to board coordinates var boardX = x - chessBoard.x; var boardY = y - chessBoard.y; var tile = chessBoard.getTileAt(boardX, boardY); if (tile && gameState.selectedPiece) { // Check if the clicked tile is a valid move var isValidMove = false; for (var i = 0; i < gameState.validMoves.length; i++) { var move = gameState.validMoves[i]; if (move.row === tile.row && move.col === tile.col) { isValidMove = true; break; } } if (isValidMove) { // Move the piece movePiece(gameState.selectedPiece, tile.row, tile.col); return; } } // Clear selection if clicking elsewhere if (gameState.selectedPiece) { chessBoard.clearHighlights(); gameState.selectedPiece = null; } }; // Game Mode Buttons var twoPlayerBtn = new Text2('Two Player', { size: 50, fill: 0xFFFFFF }); twoPlayerBtn.anchor.set(0.5, 0); twoPlayerBtn.y = 2732 - 200; twoPlayerBtn.x = 2048 / 2 - 300; LK.gui.addChild(twoPlayerBtn); twoPlayerBtn.interactive = true; twoPlayerBtn.buttonMode = true; twoPlayerBtn.on('pointerdown', function () { gameState.gameMode = 'twoPlayer'; gameState.aiPlayer = null; LK.setScore(0); initializeChessBoard(); updateTurnText(); chessBoard.clearHighlights(); }); var aiBtn = new Text2('Play vs AI', { size: 50, fill: 0xFFFFFF }); aiBtn.anchor.set(0.5, 0); aiBtn.y = 2732 - 200; aiBtn.x = 2048 / 2 + 300; LK.gui.addChild(aiBtn); aiBtn.interactive = true; aiBtn.buttonMode = true; aiBtn.on('pointerdown', function () { gameState.gameMode = 'ai'; gameState.aiPlayer = 'black'; LK.setScore(0); initializeChessBoard(); updateTurnText(); chessBoard.clearHighlights(); }); // AI Difficulty Buttons var easyBtn = new Text2('Easy', { size: 40, fill: gameState.aiDifficulty === 1 ? "#ffff00" : "#aaaaaa" }); easyBtn.style = { fill: gameState.aiDifficulty === 1 ? "#ffff00" : "#aaaaaa" }; easyBtn.anchor.set(0.5, 0); easyBtn.y = 2732 - 120; easyBtn.x = 2048 / 2 - 300; LK.gui.addChild(easyBtn); easyBtn.interactive = true; easyBtn.buttonMode = true; easyBtn.on('pointerdown', function () { gameState.aiDifficulty = 1; storage.aiDifficulty = 1; easyBtn.style.fill = "#ffff00"; mediumBtn.style.fill = "#aaaaaa"; hardBtn.style.fill = "#aaaaaa"; }); var mediumBtn = new Text2('Medium', { size: 40, fill: gameState.aiDifficulty === 2 ? "#ffff00" : "#aaaaaa" }); mediumBtn.style = { fill: gameState.aiDifficulty === 2 ? "#ffff00" : "#aaaaaa" }; mediumBtn.anchor.set(0.5, 0); mediumBtn.y = 2732 - 120; mediumBtn.x = 2048 / 2; LK.gui.addChild(mediumBtn); mediumBtn.interactive = true; mediumBtn.buttonMode = true; mediumBtn.on('pointerdown', function () { gameState.aiDifficulty = 2; storage.aiDifficulty = 2; easyBtn.style.fill = "#aaaaaa"; mediumBtn.style.fill = "#ffff00"; hardBtn.style.fill = "#aaaaaa"; }); var hardBtn = new Text2('Hard', { size: 40, fill: gameState.aiDifficulty === 3 ? "#ffff00" : "#aaaaaa" }); hardBtn.style = { fill: gameState.aiDifficulty === 3 ? "#ffff00" : "#aaaaaa" }; hardBtn.anchor.set(0.5, 0); hardBtn.y = 2732 - 120; hardBtn.x = 2048 / 2 + 300; LK.gui.addChild(hardBtn); hardBtn.interactive = true; hardBtn.buttonMode = true; hardBtn.on('pointerdown', function () { gameState.aiDifficulty = 3; storage.aiDifficulty = 3; easyBtn.style.fill = "#aaaaaa"; mediumBtn.style.fill = "#aaaaaa"; hardBtn.style.fill = "#ffff00"; }); // Update difficulty button colors based on current setting switch (gameState.aiDifficulty) { case 1: easyBtn.style.fill = "#ffff00"; mediumBtn.style.fill = "#aaaaaa"; hardBtn.style.fill = "#aaaaaa"; break; case 2: easyBtn.style.fill = "#aaaaaa"; mediumBtn.style.fill = "#ffff00"; hardBtn.style.fill = "#aaaaaa"; break; case 3: easyBtn.style.fill = "#aaaaaa"; mediumBtn.style.fill = "#aaaaaa"; hardBtn.style.fill = "#ffff00"; break; } // New Game button var newGameBtn = new Text2('New Game', { size: 50, fill: 0xFFFFFF }); newGameBtn.anchor.set(0.5, 0); newGameBtn.y = 2732 - 280; newGameBtn.x = 2048 / 2; LK.gui.addChild(newGameBtn); newGameBtn.interactive = true; newGameBtn.buttonMode = true; newGameBtn.on('pointerdown', function () { initializeChessBoard(); updateTurnText(); }); // Initialize the game initializeChessBoard(); // Main game loop game.update = function () { // Game update logic happens in event handlers and callbacks }; // Play background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
aiDifficulty: 1,
gameHistory: []
});
/****
* Classes
****/
var Board = Container.expand(function () {
var self = Container.call(this);
self.tiles = [];
self.tileSize = 256;
self.boardSize = 8 * self.tileSize;
self.highlightLayer = new Container();
self.piecesLayer = new Container();
self.createBoard = function () {
// Create tiles
for (var row = 0; row < 8; row++) {
self.tiles[row] = [];
for (var col = 0; col < 8; col++) {
var isWhite = (row + col) % 2 === 0;
var tile = self.attachAsset(isWhite ? 'whiteTile' : 'blackTile', {
x: col * self.tileSize,
y: row * self.tileSize,
anchorX: 0,
anchorY: 0
});
self.tiles[row][col] = tile;
tile.boardRow = row;
tile.boardCol = col;
// Add coordinate labels
if (row === 7) {
// Add file (column) labels at the bottom
var fileLabel = new Text2(String.fromCharCode(97 + col), {
size: 30,
fill: isWhite ? "#333333" : "#ffffff"
});
fileLabel.anchor.set(1.0, 1.0);
fileLabel.x = (col + 1) * self.tileSize - 10;
fileLabel.y = (row + 1) * self.tileSize - 10;
self.addChild(fileLabel);
}
if (col === 0) {
// Add rank (row) labels on the left
var rankLabel = new Text2(String(8 - row), {
size: 30,
fill: isWhite ? "#333333" : "#ffffff"
});
rankLabel.anchor.set(0.0, 0.0);
rankLabel.x = col * self.tileSize + 10;
rankLabel.y = row * self.tileSize + 10;
self.addChild(rankLabel);
}
}
}
self.addChild(self.highlightLayer);
self.addChild(self.piecesLayer);
};
self.highlightTile = function (row, col, type) {
var highlight = LK.getAsset(type, {
x: col * self.tileSize,
y: row * self.tileSize,
anchorX: 0,
anchorY: 0,
alpha: 0.5
});
self.highlightLayer.addChild(highlight);
return highlight;
};
self.clearHighlights = function () {
while (self.highlightLayer.children.length > 0) {
self.highlightLayer.removeChildAt(0);
}
};
self.getTileAt = function (x, y) {
var col = Math.floor(x / self.tileSize);
var row = Math.floor(y / self.tileSize);
if (row >= 0 && row < 8 && col >= 0 && col < 8) {
return {
row: row,
col: col
};
}
return null;
};
return self;
});
var Piece = Container.expand(function (type, color, row, col) {
var self = Container.call(this);
self.type = type;
self.color = color;
self.row = row;
self.col = col;
self.hasMoved = false;
self.getPieceSymbol = function () {
switch (self.type) {
case 'king':
return '♚';
case 'queen':
return '♛';
case 'rook':
return '♜';
case 'bishop':
return '♝';
case 'knight':
return '♞';
case 'pawn':
return '♟';
default:
return '';
}
};
// Get asset id based on color and type
var assetId = color + type.charAt(0).toUpperCase() + type.slice(1);
self.sprite = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add chess piece symbol
self.symbol = new Text2(self.getPieceSymbol(), {
size: 150,
fill: color === 'white' ? "#333333" : "#ffffff"
});
self.symbol.anchor.set(0.5, 0.5);
self.addChild(self.symbol);
self.moveTo = function (row, col, duration) {
self.row = row;
self.col = col;
var newX = col * chessBoard.tileSize + chessBoard.tileSize / 2;
var newY = row * chessBoard.tileSize + chessBoard.tileSize / 2;
if (duration) {
tween(self, {
x: newX,
y: newY
}, {
duration: duration,
easing: tween.easeOutQuad
});
} else {
self.x = newX;
self.y = newY;
}
self.hasMoved = true;
};
self.update = function () {
// For any per-frame updates
};
self.down = function (x, y, obj) {
if (gameState.currentTurn === self.color && !gameState.isAnimating) {
gameState.selectedPiece = self;
chessBoard.clearHighlights();
// Highlight the current piece's tile
self.currentHighlight = chessBoard.highlightTile(self.row, self.col, 'highlightTile');
// Highlight valid moves
var validMoves = getValidMoves(self);
gameState.validMoves = validMoves;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
chessBoard.highlightTile(move.row, move.col, 'validMoveTile');
}
}
};
// Place piece at its position
self.x = col * 256 + 128;
self.y = row * 256 + 128;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game state
var gameState = {
currentTurn: 'white',
selectedPiece: null,
validMoves: [],
board: Array(8).fill().map(function () {
return Array(8).fill(null);
}),
isAnimating: false,
lastMove: null,
aiDifficulty: storage.aiDifficulty || 1,
gameMode: 'ai',
// 'ai' or 'twoPlayer'
status: 'playing',
// 'playing', 'check', 'checkmate', 'stalemate'
aiPlayer: 'black'
};
// Create UI
var titleText = new Text2('Chess Challenge', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
LK.gui.top.addChild(titleText);
var turnText = new Text2('White to move', {
size: 50,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.y = 100;
LK.gui.top.addChild(turnText);
var statusText = new Text2('', {
size: 50,
fill: 0xFFFF00
});
statusText.anchor.set(0.5, 0);
statusText.y = 160;
LK.gui.top.addChild(statusText);
// Create and position the chess board
var chessBoard = new Board();
game.addChild(chessBoard);
chessBoard.createBoard();
// Center the board
chessBoard.x = (2048 - chessBoard.boardSize) / 2;
chessBoard.y = (2732 - chessBoard.boardSize) / 2;
// Initialize pieces
function initializeChessBoard() {
// Clear existing pieces
while (chessBoard.piecesLayer.children.length > 0) {
chessBoard.piecesLayer.removeChildAt(0);
}
// Reset game state
gameState.board = Array(8).fill().map(function () {
return Array(8).fill(null);
});
gameState.currentTurn = 'white';
gameState.selectedPiece = null;
gameState.validMoves = [];
gameState.isAnimating = false;
gameState.lastMove = null;
gameState.status = 'playing';
// Update UI
updateTurnText();
// Create pieces
var pieces = [{
type: 'rook',
color: 'black',
row: 0,
col: 0
}, {
type: 'knight',
color: 'black',
row: 0,
col: 1
}, {
type: 'bishop',
color: 'black',
row: 0,
col: 2
}, {
type: 'queen',
color: 'black',
row: 0,
col: 3
}, {
type: 'king',
color: 'black',
row: 0,
col: 4
}, {
type: 'bishop',
color: 'black',
row: 0,
col: 5
}, {
type: 'knight',
color: 'black',
row: 0,
col: 6
}, {
type: 'rook',
color: 'black',
row: 0,
col: 7
}, {
type: 'rook',
color: 'white',
row: 7,
col: 0
}, {
type: 'knight',
color: 'white',
row: 7,
col: 1
}, {
type: 'bishop',
color: 'white',
row: 7,
col: 2
}, {
type: 'queen',
color: 'white',
row: 7,
col: 3
}, {
type: 'king',
color: 'white',
row: 7,
col: 4
}, {
type: 'bishop',
color: 'white',
row: 7,
col: 5
}, {
type: 'knight',
color: 'white',
row: 7,
col: 6
}, {
type: 'rook',
color: 'white',
row: 7,
col: 7
}];
// Add pawns
for (var i = 0; i < 8; i++) {
pieces.push({
type: 'pawn',
color: 'black',
row: 1,
col: i
});
pieces.push({
type: 'pawn',
color: 'white',
row: 6,
col: i
});
}
// Create and position pieces
for (var i = 0; i < pieces.length; i++) {
var p = pieces[i];
var piece = new Piece(p.type, p.color, p.row, p.col);
chessBoard.piecesLayer.addChild(piece);
gameState.board[p.row][p.col] = piece;
}
// Clear highlights
chessBoard.clearHighlights();
}
// Chess move validation
function getValidMoves(piece) {
var moves = [];
if (!piece) return moves;
function addMove(row, col) {
moves.push({
row: row,
col: col
});
}
function isOnBoard(row, col) {
return row >= 0 && row < 8 && col >= 0 && col < 8;
}
function isOccupied(row, col) {
return gameState.board[row][col] !== null;
}
function isEnemyPiece(row, col) {
return isOccupied(row, col) && gameState.board[row][col].color !== piece.color;
}
function isFriendlyPiece(row, col) {
return isOccupied(row, col) && gameState.board[row][col].color === piece.color;
}
var row = piece.row;
var col = piece.col;
switch (piece.type) {
case 'pawn':
var direction = piece.color === 'white' ? -1 : 1;
// Move forward one square
if (isOnBoard(row + direction, col) && !isOccupied(row + direction, col)) {
addMove(row + direction, col);
// Move forward two squares on first move
if (!piece.hasMoved && isOnBoard(row + 2 * direction, col) && !isOccupied(row + 2 * direction, col)) {
addMove(row + 2 * direction, col);
}
}
// Capture diagonally
if (isOnBoard(row + direction, col - 1) && isEnemyPiece(row + direction, col - 1)) {
addMove(row + direction, col - 1);
}
if (isOnBoard(row + direction, col + 1) && isEnemyPiece(row + direction, col + 1)) {
addMove(row + direction, col + 1);
}
// En passant (simplified)
if (gameState.lastMove && gameState.lastMove.piece.type === 'pawn' && gameState.lastMove.fromRow === (piece.color === 'white' ? 1 : 6) && gameState.lastMove.toRow === (piece.color === 'white' ? 3 : 4) && Math.abs(gameState.lastMove.toCol - col) === 1 && row === gameState.lastMove.toRow) {
addMove(row + direction, gameState.lastMove.toCol);
}
break;
case 'rook':
// Horizontal and vertical directions
var directions = [{
dr: -1,
dc: 0
},
// up
{
dr: 1,
dc: 0
},
// down
{
dr: 0,
dc: -1
},
// left
{
dr: 0,
dc: 1
} // right
];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
while (isOnBoard(r, c)) {
if (isOccupied(r, c)) {
if (isEnemyPiece(r, c)) {
addMove(r, c);
}
break;
}
addMove(r, c);
r += dir.dr;
c += dir.dc;
}
});
break;
case 'knight':
// Knight moves
var knightMoves = [{
dr: -2,
dc: -1
}, {
dr: -2,
dc: 1
}, {
dr: -1,
dc: -2
}, {
dr: -1,
dc: 2
}, {
dr: 1,
dc: -2
}, {
dr: 1,
dc: 2
}, {
dr: 2,
dc: -1
}, {
dr: 2,
dc: 1
}];
knightMoves.forEach(function (move) {
var r = row + move.dr;
var c = col + move.dc;
if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) {
addMove(r, c);
}
});
break;
case 'bishop':
// Diagonal directions
var directions = [{
dr: -1,
dc: -1
},
// up-left
{
dr: -1,
dc: 1
},
// up-right
{
dr: 1,
dc: -1
},
// down-left
{
dr: 1,
dc: 1
} // down-right
];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
while (isOnBoard(r, c)) {
if (isOccupied(r, c)) {
if (isEnemyPiece(r, c)) {
addMove(r, c);
}
break;
}
addMove(r, c);
r += dir.dr;
c += dir.dc;
}
});
break;
case 'queen':
// Combine rook and bishop moves
var directions = [{
dr: -1,
dc: 0
}, {
dr: 1,
dc: 0
},
// vertical
{
dr: 0,
dc: -1
}, {
dr: 0,
dc: 1
},
// horizontal
{
dr: -1,
dc: -1
}, {
dr: -1,
dc: 1
},
// diagonal
{
dr: 1,
dc: -1
}, {
dr: 1,
dc: 1
} // diagonal
];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
while (isOnBoard(r, c)) {
if (isOccupied(r, c)) {
if (isEnemyPiece(r, c)) {
addMove(r, c);
}
break;
}
addMove(r, c);
r += dir.dr;
c += dir.dc;
}
});
break;
case 'king':
// King moves (one square in any direction)
var directions = [{
dr: -1,
dc: -1
}, {
dr: -1,
dc: 0
}, {
dr: -1,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 0,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: 1,
dc: 1
}];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) {
addMove(r, c);
}
});
// Castling
if (!piece.hasMoved) {
// Kingside castling
if (!isOccupied(row, col + 1) && !isOccupied(row, col + 2) && isOccupied(row, col + 3) && !gameState.board[row][col + 3].hasMoved && gameState.board[row][col + 3].type === 'rook') {
addMove(row, col + 2);
}
// Queenside castling
if (!isOccupied(row, col - 1) && !isOccupied(row, col - 2) && !isOccupied(row, col - 3) && isOccupied(row, col - 4) && !gameState.board[row][col - 4].hasMoved && gameState.board[row][col - 4].type === 'rook') {
addMove(row, col - 2);
}
}
break;
}
// Filter out moves that would put the king in check
var validMoves = [];
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
// Make a temporary move
var tempPiece = gameState.board[move.row][move.col];
gameState.board[move.row][move.col] = piece;
gameState.board[row][col] = null;
// Check if the move puts/leaves the king in check
var inCheck = isKingInCheck(piece.color);
// Undo the move
gameState.board[row][col] = piece;
gameState.board[move.row][move.col] = tempPiece;
if (!inCheck) {
validMoves.push(move);
}
}
return validMoves;
}
function isKingInCheck(color) {
// Find the king
var kingRow = -1;
var kingCol = -1;
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (piece && piece.type === 'king' && piece.color === color) {
kingRow = r;
kingCol = c;
break;
}
}
if (kingRow !== -1) break;
}
// Check if any enemy piece can capture the king
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (piece && piece.color !== color) {
// Get the enemy's valid moves (ignoring check)
var moves = getBasicMoves(piece);
// See if any of the moves can capture the king
for (var i = 0; i < moves.length; i++) {
if (moves[i].row === kingRow && moves[i].col === kingCol) {
return true;
}
}
}
}
}
return false;
}
function getBasicMoves(piece) {
var moves = [];
if (!piece) return moves;
function addMove(row, col) {
moves.push({
row: row,
col: col
});
}
function isOnBoard(row, col) {
return row >= 0 && row < 8 && col >= 0 && col < 8;
}
function isOccupied(row, col) {
return gameState.board[row][col] !== null;
}
function isEnemyPiece(row, col) {
return isOccupied(row, col) && gameState.board[row][col].color !== piece.color;
}
function isFriendlyPiece(row, col) {
return isOccupied(row, col) && gameState.board[row][col].color === piece.color;
}
var row = piece.row;
var col = piece.col;
switch (piece.type) {
case 'pawn':
var direction = piece.color === 'white' ? -1 : 1;
// Move forward one square
if (isOnBoard(row + direction, col) && !isOccupied(row + direction, col)) {
addMove(row + direction, col);
// Move forward two squares on first move
if (!piece.hasMoved && isOnBoard(row + 2 * direction, col) && !isOccupied(row + 2 * direction, col)) {
addMove(row + 2 * direction, col);
}
}
// Capture diagonally
if (isOnBoard(row + direction, col - 1) && isEnemyPiece(row + direction, col - 1)) {
addMove(row + direction, col - 1);
}
if (isOnBoard(row + direction, col + 1) && isEnemyPiece(row + direction, col + 1)) {
addMove(row + direction, col + 1);
}
break;
case 'rook':
// Horizontal and vertical directions
var directions = [{
dr: -1,
dc: 0
},
// up
{
dr: 1,
dc: 0
},
// down
{
dr: 0,
dc: -1
},
// left
{
dr: 0,
dc: 1
} // right
];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
while (isOnBoard(r, c)) {
if (isOccupied(r, c)) {
if (isEnemyPiece(r, c)) {
addMove(r, c);
}
break;
}
addMove(r, c);
r += dir.dr;
c += dir.dc;
}
});
break;
case 'knight':
// Knight moves
var knightMoves = [{
dr: -2,
dc: -1
}, {
dr: -2,
dc: 1
}, {
dr: -1,
dc: -2
}, {
dr: -1,
dc: 2
}, {
dr: 1,
dc: -2
}, {
dr: 1,
dc: 2
}, {
dr: 2,
dc: -1
}, {
dr: 2,
dc: 1
}];
knightMoves.forEach(function (move) {
var r = row + move.dr;
var c = col + move.dc;
if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) {
addMove(r, c);
}
});
break;
case 'bishop':
// Diagonal directions
var directions = [{
dr: -1,
dc: -1
},
// up-left
{
dr: -1,
dc: 1
},
// up-right
{
dr: 1,
dc: -1
},
// down-left
{
dr: 1,
dc: 1
} // down-right
];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
while (isOnBoard(r, c)) {
if (isOccupied(r, c)) {
if (isEnemyPiece(r, c)) {
addMove(r, c);
}
break;
}
addMove(r, c);
r += dir.dr;
c += dir.dc;
}
});
break;
case 'queen':
// Combine rook and bishop moves
var directions = [{
dr: -1,
dc: 0
}, {
dr: 1,
dc: 0
},
// vertical
{
dr: 0,
dc: -1
}, {
dr: 0,
dc: 1
},
// horizontal
{
dr: -1,
dc: -1
}, {
dr: -1,
dc: 1
},
// diagonal
{
dr: 1,
dc: -1
}, {
dr: 1,
dc: 1
} // diagonal
];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
while (isOnBoard(r, c)) {
if (isOccupied(r, c)) {
if (isEnemyPiece(r, c)) {
addMove(r, c);
}
break;
}
addMove(r, c);
r += dir.dr;
c += dir.dc;
}
});
break;
case 'king':
// King moves (one square in any direction)
var directions = [{
dr: -1,
dc: -1
}, {
dr: -1,
dc: 0
}, {
dr: -1,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 0,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: 1,
dc: 1
}];
directions.forEach(function (dir) {
var r = row + dir.dr;
var c = col + dir.dc;
if (isOnBoard(r, c) && !isFriendlyPiece(r, c)) {
addMove(r, c);
}
});
break;
}
return moves;
}
function isCheckmate(color) {
if (!isKingInCheck(color)) return false;
// Check if any piece can make a valid move
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (piece && piece.color === color) {
var validMoves = getValidMoves(piece);
if (validMoves.length > 0) {
return false;
}
}
}
}
return true;
}
function isStalemate(color) {
if (isKingInCheck(color)) return false;
// Check if any piece can make a valid move
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (piece && piece.color === color) {
var validMoves = getValidMoves(piece);
if (validMoves.length > 0) {
return false;
}
}
}
}
return true;
}
function updateGameStatus() {
if (isCheckmate(gameState.currentTurn)) {
gameState.status = 'checkmate';
var winner = gameState.currentTurn === 'white' ? 'Black' : 'White';
statusText.setText(winner + ' wins by checkmate!');
LK.getSound('gameEnd').play();
// Update score if AI was played against
if (gameState.gameMode === 'ai') {
if (winner === 'White' && gameState.aiPlayer === 'black' || winner === 'Black' && gameState.aiPlayer === 'white') {
LK.setScore(LK.getScore() + 1);
}
}
return true;
} else if (isStalemate(gameState.currentTurn)) {
gameState.status = 'stalemate';
statusText.setText('Game drawn by stalemate!');
LK.getSound('gameEnd').play();
return true;
} else if (isKingInCheck(gameState.currentTurn)) {
gameState.status = 'check';
statusText.setText(gameState.currentTurn.charAt(0).toUpperCase() + gameState.currentTurn.slice(1) + ' is in check!');
LK.getSound('check').play();
} else {
gameState.status = 'playing';
statusText.setText('');
}
return false;
}
function updateTurnText() {
var turnName = gameState.currentTurn.charAt(0).toUpperCase() + gameState.currentTurn.slice(1);
turnText.setText(turnName + ' to move');
}
function movePiece(piece, targetRow, targetCol) {
var isCapture = gameState.board[targetRow][targetCol] !== null;
var capturedPiece = gameState.board[targetRow][targetCol];
var fromRow = piece.row;
var fromCol = piece.col;
// Handle castling
if (piece.type === 'king' && Math.abs(targetCol - fromCol) === 2) {
// Kingside castling
if (targetCol > fromCol) {
var rook = gameState.board[fromRow][7];
gameState.board[fromRow][5] = rook;
gameState.board[fromRow][7] = null;
rook.moveTo(fromRow, 5, 300);
}
// Queenside castling
else {
var rook = gameState.board[fromRow][0];
gameState.board[fromRow][3] = rook;
gameState.board[fromRow][0] = null;
rook.moveTo(fromRow, 3, 300);
}
}
// Handle en passant capture
if (piece.type === 'pawn' && Math.abs(targetCol - fromCol) === 1 && !isCapture) {
var enPassantRow = piece.color === 'white' ? targetRow + 1 : targetRow - 1;
capturedPiece = gameState.board[enPassantRow][targetCol];
gameState.board[enPassantRow][targetCol] = null;
if (capturedPiece) {
capturedPiece.parent.removeChild(capturedPiece);
}
isCapture = true;
}
// Update board array
gameState.board[fromRow][fromCol] = null;
if (capturedPiece) {
capturedPiece.parent.removeChild(capturedPiece);
}
gameState.board[targetRow][targetCol] = piece;
// Move the piece with animation
gameState.isAnimating = true;
piece.moveTo(targetRow, targetCol, 300);
// Play sound
if (isCapture) {
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Handle pawn promotion (always to queen for simplicity)
if (piece.type === 'pawn' && (targetRow === 0 || targetRow === 7)) {
// Wait for the piece to finish moving
var promotionTimeout = LK.setTimeout(function () {
// Remove the pawn
piece.parent.removeChild(piece);
gameState.board[targetRow][targetCol] = null;
// Create a new queen
var queen = new Piece('queen', piece.color, targetRow, targetCol);
chessBoard.piecesLayer.addChild(queen);
gameState.board[targetRow][targetCol] = queen;
LK.clearTimeout(promotionTimeout);
}, 350);
}
// Update last move
gameState.lastMove = {
piece: piece,
fromRow: fromRow,
fromCol: fromCol,
toRow: targetRow,
toCol: targetCol
};
// Highlight the last move
var moveTimeout = LK.setTimeout(function () {
chessBoard.clearHighlights();
chessBoard.highlightTile(fromRow, fromCol, 'lastMoveTile');
chessBoard.highlightTile(targetRow, targetCol, 'lastMoveTile');
// Switch turns
gameState.currentTurn = gameState.currentTurn === 'white' ? 'black' : 'white';
updateTurnText();
// Check for check, checkmate, stalemate
var gameEnded = updateGameStatus();
gameState.isAnimating = false;
gameState.selectedPiece = null;
// If AI is playing and it's AI's turn
if (!gameEnded && gameState.gameMode === 'ai' && gameState.currentTurn === gameState.aiPlayer) {
makeAIMove();
}
LK.clearTimeout(moveTimeout);
}, 350);
}
function makeAIMove() {
var aiTimeout = LK.setTimeout(function () {
var allPossibleMoves = [];
// Collect all possible moves for AI pieces
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (piece && piece.color === gameState.aiPlayer) {
var validMoves = getValidMoves(piece);
for (var i = 0; i < validMoves.length; i++) {
allPossibleMoves.push({
piece: piece,
move: validMoves[i]
});
}
}
}
}
// Piece value tables - used to evaluate position advantages for each piece type
var piecePositionValues = {
pawn: [[0, 0, 0, 0, 0, 0, 0, 0], [50, 50, 50, 50, 50, 50, 50, 50], [10, 10, 20, 30, 30, 20, 10, 10], [5, 5, 10, 25, 25, 10, 5, 5], [0, 0, 0, 20, 20, 0, 0, 0], [5, -5, -10, 0, 0, -10, -5, 5], [5, 10, 10, -20, -20, 10, 10, 5], [0, 0, 0, 0, 0, 0, 0, 0]],
knight: [[-50, -40, -30, -30, -30, -30, -40, -50], [-40, -20, 0, 0, 0, 0, -20, -40], [-30, 0, 10, 15, 15, 10, 0, -30], [-30, 5, 15, 20, 20, 15, 5, -30], [-30, 0, 15, 20, 20, 15, 0, -30], [-30, 5, 10, 15, 15, 10, 5, -30], [-40, -20, 0, 5, 5, 0, -20, -40], [-50, -40, -30, -30, -30, -30, -40, -50]],
bishop: [[-20, -10, -10, -10, -10, -10, -10, -20], [-10, 0, 0, 0, 0, 0, 0, -10], [-10, 0, 10, 10, 10, 10, 0, -10], [-10, 5, 5, 10, 10, 5, 5, -10], [-10, 0, 5, 10, 10, 5, 0, -10], [-10, 5, 5, 5, 5, 5, 5, -10], [-10, 0, 5, 0, 0, 5, 0, -10], [-20, -10, -10, -10, -10, -10, -10, -20]],
rook: [[0, 0, 0, 0, 0, 0, 0, 0], [5, 10, 10, 10, 10, 10, 10, 5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [-5, 0, 0, 0, 0, 0, 0, -5], [0, 0, 0, 5, 5, 0, 0, 0]],
queen: [[-20, -10, -10, -5, -5, -10, -10, -20], [-10, 0, 0, 0, 0, 0, 0, -10], [-10, 0, 5, 5, 5, 5, 0, -10], [-5, 0, 5, 5, 5, 5, 0, -5], [0, 0, 5, 5, 5, 5, 0, -5], [-10, 5, 5, 5, 5, 5, 0, -10], [-10, 0, 5, 0, 0, 0, 0, -10], [-20, -10, -10, -5, -5, -10, -10, -20]],
king: [[-30, -40, -40, -50, -50, -40, -40, -30], [-30, -40, -40, -50, -50, -40, -40, -30], [-30, -40, -40, -50, -50, -40, -40, -30], [-30, -40, -40, -50, -50, -40, -40, -30], [-20, -30, -30, -40, -40, -30, -30, -20], [-10, -20, -20, -20, -20, -20, -20, -10], [20, 20, 0, 0, 0, 0, 20, 20], [20, 30, 10, 0, 0, 10, 30, 20]]
};
// Base piece values
var pieceValues = {
pawn: 100,
knight: 320,
bishop: 330,
rook: 500,
queen: 900,
king: 20000
};
if (allPossibleMoves.length > 0) {
var selectedMove;
// Basic AI difficulty levels
if (gameState.aiDifficulty === 1) {
// Easy: Random move with slight preference for captures
var captureMoves = allPossibleMoves.filter(function (moveObj) {
return gameState.board[moveObj.move.row][moveObj.move.col] !== null;
});
if (captureMoves.length > 0 && Math.random() > 0.5) {
selectedMove = captureMoves[Math.floor(Math.random() * captureMoves.length)];
} else {
selectedMove = allPossibleMoves[Math.floor(Math.random() * allPossibleMoves.length)];
}
} else if (gameState.aiDifficulty === 2) {
// Medium: Prefer captures and positional advantages
var moveScores = [];
for (var i = 0; i < allPossibleMoves.length; i++) {
var moveObj = allPossibleMoves[i];
var targetPiece = gameState.board[moveObj.move.row][moveObj.move.col];
var score = 0;
// Capture value
if (targetPiece) {
score += pieceValues[targetPiece.type];
}
// Position value (flipped for black pieces)
var row = moveObj.move.row;
var col = moveObj.move.col;
if (gameState.aiPlayer === 'black') {
row = 7 - row;
}
score += piecePositionValues[moveObj.piece.type][row][col] * 0.5;
moveScores.push({
move: moveObj,
score: score
});
}
// Sort moves by score
moveScores.sort(function (a, b) {
return b.score - a.score;
});
// Pick from top 3 moves to add some randomness
var topMoves = moveScores.slice(0, Math.min(3, moveScores.length));
selectedMove = topMoves[Math.floor(Math.random() * topMoves.length)].move;
} else {
// Hard: Minimax with alpha-beta pruning (simplified)
var bestScore = -Infinity;
var bestMoves = [];
for (var i = 0; i < allPossibleMoves.length; i++) {
var moveObj = allPossibleMoves[i];
var piece = moveObj.piece;
var targetRow = moveObj.move.row;
var targetCol = moveObj.move.col;
var originalRow = piece.row;
var originalCol = piece.col;
var capturedPiece = gameState.board[targetRow][targetCol];
// Make temporary move
gameState.board[targetRow][targetCol] = piece;
gameState.board[originalRow][originalCol] = null;
// Evaluate board after move
var score = evaluateBoard(gameState.aiPlayer);
// Unmake the move
gameState.board[originalRow][originalCol] = piece;
gameState.board[targetRow][targetCol] = capturedPiece;
if (score > bestScore) {
bestScore = score;
bestMoves = [moveObj];
} else if (score === bestScore) {
bestMoves.push(moveObj);
}
}
if (bestMoves.length > 0) {
selectedMove = bestMoves[Math.floor(Math.random() * bestMoves.length)];
} else {
selectedMove = allPossibleMoves[Math.floor(Math.random() * allPossibleMoves.length)];
}
}
// Make the selected move
movePiece(selectedMove.piece, selectedMove.move.row, selectedMove.move.col);
}
LK.clearTimeout(aiTimeout);
}, 500);
// Function to evaluate the current board position
function evaluateBoard(playerColor) {
var score = 0;
var opponentColor = playerColor === 'white' ? 'black' : 'white';
// Count material and positional advantages
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (!piece) continue;
var value = pieceValues[piece.type];
// Add position value
var row = r;
var col = c;
if (piece.color === 'black') {
row = 7 - r; // Flip board for black
}
var positionValue = piecePositionValues[piece.type][row][col];
// Add to score if our piece, subtract if opponent's
if (piece.color === playerColor) {
score += value + positionValue;
// Bonus for attacking opponent pieces
var attackedSquares = getAttackedSquares(piece);
for (var i = 0; i < attackedSquares.length; i++) {
var attackedPiece = gameState.board[attackedSquares[i].row][attackedSquares[i].col];
if (attackedPiece && attackedPiece.color === opponentColor) {
score += value * 0.1; // Small bonus for attacking
// Higher bonus for attacking higher value pieces
score += pieceValues[attackedPiece.type] * 0.05;
}
}
// Bonus for piece mobility (number of legal moves)
score += getValidMoves(piece).length * 2;
// Bonus for developed pieces
if (piece.type === 'knight' || piece.type === 'bishop') {
var initialRow = playerColor === 'white' ? 7 : 0;
if (piece.row !== initialRow) {
score += 15; // Developed minor piece
}
}
} else {
score -= value + positionValue;
}
}
}
// Check if opponent is in check
var tempTurn = gameState.currentTurn;
gameState.currentTurn = opponentColor;
if (isKingInCheck(opponentColor)) {
score += 50; // Bonus for putting opponent in check
// Check if it's checkmate (huge bonus)
if (isCheckmate(opponentColor)) {
score += 10000;
}
}
// Penalize if our king is in check
gameState.currentTurn = playerColor;
if (isKingInCheck(playerColor)) {
score -= 60; // Penalty for being in check
}
gameState.currentTurn = tempTurn;
// Pawn structure evaluation
score += evaluatePawnStructure(playerColor) - evaluatePawnStructure(opponentColor);
// Control of center
score += evaluateCenterControl(playerColor) - evaluateCenterControl(opponentColor);
return score;
}
// Evaluate pawn structure
function evaluatePawnStructure(color) {
var score = 0;
var pawnColumns = [];
// Find all pawns
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var piece = gameState.board[r][c];
if (piece && piece.type === 'pawn' && piece.color === color) {
pawnColumns.push(c);
// Bonus for passed pawns (no enemy pawns ahead)
var passed = true;
var direction = color === 'white' ? -1 : 1;
for (var checkRow = r + direction; checkRow >= 0 && checkRow < 8; checkRow += direction) {
for (var checkCol = c - 1; checkCol <= c + 1; checkCol += 2) {
if (checkCol >= 0 && checkCol < 8) {
var checkPiece = gameState.board[checkRow][checkCol];
if (checkPiece && checkPiece.type === 'pawn' && checkPiece.color !== color) {
passed = false;
break;
}
}
}
if (!passed) break;
}
if (passed) {
score += 30; // Passed pawn bonus
// Bigger bonus for pawns closer to promotion
var promotionDistance = color === 'white' ? r : 7 - r;
score += (7 - promotionDistance) * 5;
}
}
}
}
// Penalize doubled pawns (multiple pawns in same column)
var columnCounts = {};
for (var i = 0; i < pawnColumns.length; i++) {
var col = pawnColumns[i];
columnCounts[col] = (columnCounts[col] || 0) + 1;
}
for (var col in columnCounts) {
if (columnCounts[col] > 1) {
score -= 15 * (columnCounts[col] - 1); // Penalty for doubled pawns
}
}
return score;
}
// Evaluate center control
function evaluateCenterControl(color) {
var score = 0;
var centerSquares = [{
row: 3,
col: 3
}, {
row: 3,
col: 4
}, {
row: 4,
col: 3
}, {
row: 4,
col: 4
}];
for (var i = 0; i < centerSquares.length; i++) {
var square = centerSquares[i];
var piece = gameState.board[square.row][square.col];
// Bonus for occupying center
if (piece && piece.color === color) {
score += 10;
}
// Bonus for attacking center
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
var attackingPiece = gameState.board[r][c];
if (attackingPiece && attackingPiece.color === color) {
var attackedSquares = getAttackedSquares(attackingPiece);
for (var j = 0; j < attackedSquares.length; j++) {
if (attackedSquares[j].row === square.row && attackedSquares[j].col === square.col) {
score += 5;
break;
}
}
}
}
}
}
return score;
}
// Get squares a piece can attack
function getAttackedSquares(piece) {
return getBasicMoves(piece);
}
}
// Game event handlers
game.down = function (x, y, obj) {
if (gameState.isAnimating) return;
// Convert screen coordinates to board coordinates
var boardX = x - chessBoard.x;
var boardY = y - chessBoard.y;
var tile = chessBoard.getTileAt(boardX, boardY);
if (tile && gameState.selectedPiece) {
// Check if the clicked tile is a valid move
var isValidMove = false;
for (var i = 0; i < gameState.validMoves.length; i++) {
var move = gameState.validMoves[i];
if (move.row === tile.row && move.col === tile.col) {
isValidMove = true;
break;
}
}
if (isValidMove) {
// Move the piece
movePiece(gameState.selectedPiece, tile.row, tile.col);
return;
}
}
// Clear selection if clicking elsewhere
if (gameState.selectedPiece) {
chessBoard.clearHighlights();
gameState.selectedPiece = null;
}
};
// Game Mode Buttons
var twoPlayerBtn = new Text2('Two Player', {
size: 50,
fill: 0xFFFFFF
});
twoPlayerBtn.anchor.set(0.5, 0);
twoPlayerBtn.y = 2732 - 200;
twoPlayerBtn.x = 2048 / 2 - 300;
LK.gui.addChild(twoPlayerBtn);
twoPlayerBtn.interactive = true;
twoPlayerBtn.buttonMode = true;
twoPlayerBtn.on('pointerdown', function () {
gameState.gameMode = 'twoPlayer';
gameState.aiPlayer = null;
LK.setScore(0);
initializeChessBoard();
updateTurnText();
chessBoard.clearHighlights();
});
var aiBtn = new Text2('Play vs AI', {
size: 50,
fill: 0xFFFFFF
});
aiBtn.anchor.set(0.5, 0);
aiBtn.y = 2732 - 200;
aiBtn.x = 2048 / 2 + 300;
LK.gui.addChild(aiBtn);
aiBtn.interactive = true;
aiBtn.buttonMode = true;
aiBtn.on('pointerdown', function () {
gameState.gameMode = 'ai';
gameState.aiPlayer = 'black';
LK.setScore(0);
initializeChessBoard();
updateTurnText();
chessBoard.clearHighlights();
});
// AI Difficulty Buttons
var easyBtn = new Text2('Easy', {
size: 40,
fill: gameState.aiDifficulty === 1 ? "#ffff00" : "#aaaaaa"
});
easyBtn.style = {
fill: gameState.aiDifficulty === 1 ? "#ffff00" : "#aaaaaa"
};
easyBtn.anchor.set(0.5, 0);
easyBtn.y = 2732 - 120;
easyBtn.x = 2048 / 2 - 300;
LK.gui.addChild(easyBtn);
easyBtn.interactive = true;
easyBtn.buttonMode = true;
easyBtn.on('pointerdown', function () {
gameState.aiDifficulty = 1;
storage.aiDifficulty = 1;
easyBtn.style.fill = "#ffff00";
mediumBtn.style.fill = "#aaaaaa";
hardBtn.style.fill = "#aaaaaa";
});
var mediumBtn = new Text2('Medium', {
size: 40,
fill: gameState.aiDifficulty === 2 ? "#ffff00" : "#aaaaaa"
});
mediumBtn.style = {
fill: gameState.aiDifficulty === 2 ? "#ffff00" : "#aaaaaa"
};
mediumBtn.anchor.set(0.5, 0);
mediumBtn.y = 2732 - 120;
mediumBtn.x = 2048 / 2;
LK.gui.addChild(mediumBtn);
mediumBtn.interactive = true;
mediumBtn.buttonMode = true;
mediumBtn.on('pointerdown', function () {
gameState.aiDifficulty = 2;
storage.aiDifficulty = 2;
easyBtn.style.fill = "#aaaaaa";
mediumBtn.style.fill = "#ffff00";
hardBtn.style.fill = "#aaaaaa";
});
var hardBtn = new Text2('Hard', {
size: 40,
fill: gameState.aiDifficulty === 3 ? "#ffff00" : "#aaaaaa"
});
hardBtn.style = {
fill: gameState.aiDifficulty === 3 ? "#ffff00" : "#aaaaaa"
};
hardBtn.anchor.set(0.5, 0);
hardBtn.y = 2732 - 120;
hardBtn.x = 2048 / 2 + 300;
LK.gui.addChild(hardBtn);
hardBtn.interactive = true;
hardBtn.buttonMode = true;
hardBtn.on('pointerdown', function () {
gameState.aiDifficulty = 3;
storage.aiDifficulty = 3;
easyBtn.style.fill = "#aaaaaa";
mediumBtn.style.fill = "#aaaaaa";
hardBtn.style.fill = "#ffff00";
});
// Update difficulty button colors based on current setting
switch (gameState.aiDifficulty) {
case 1:
easyBtn.style.fill = "#ffff00";
mediumBtn.style.fill = "#aaaaaa";
hardBtn.style.fill = "#aaaaaa";
break;
case 2:
easyBtn.style.fill = "#aaaaaa";
mediumBtn.style.fill = "#ffff00";
hardBtn.style.fill = "#aaaaaa";
break;
case 3:
easyBtn.style.fill = "#aaaaaa";
mediumBtn.style.fill = "#aaaaaa";
hardBtn.style.fill = "#ffff00";
break;
}
// New Game button
var newGameBtn = new Text2('New Game', {
size: 50,
fill: 0xFFFFFF
});
newGameBtn.anchor.set(0.5, 0);
newGameBtn.y = 2732 - 280;
newGameBtn.x = 2048 / 2;
LK.gui.addChild(newGameBtn);
newGameBtn.interactive = true;
newGameBtn.buttonMode = true;
newGameBtn.on('pointerdown', function () {
initializeChessBoard();
updateTurnText();
});
// Initialize the game
initializeChessBoard();
// Main game loop
game.update = function () {
// Game update logic happens in event handlers and callbacks
};
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});