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