/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ChessPiece = Container.expand(function (type, color, row, col) {
var self = Container.call(this);
self.pieceType = type;
self.color = color;
self.row = row;
self.col = col;
self.hasMoved = false;
var assetName = color + type.charAt(0).toUpperCase() + type.slice(1);
self.graphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.setPosition = function (row, col) {
self.row = row;
self.col = col;
if (playerPerspective === 'black') {
// Flip the board for black player perspective
self.x = boardOffsetX + (7 - col) * 200 + 100;
self.y = boardOffsetY + (7 - row) * 200 + 100;
} else {
self.x = boardOffsetX + col * 200 + 100;
self.y = boardOffsetY + row * 200 + 100;
}
};
self.getValidMoves = function () {
var moves = [];
if (self.pieceType === 'pawn') {
// Pawns move diagonally forward, capture straight forward
var direction = self.color === 'white' ? -1 : 1;
var newRow = self.row + direction;
// Diagonal movement (normal pawn movement)
if (newRow >= 0 && newRow < 8) {
if (self.col > 0 && !board[newRow][self.col - 1]) {
moves.push({
row: newRow,
col: self.col - 1
});
}
if (self.col < 7 && !board[newRow][self.col + 1]) {
moves.push({
row: newRow,
col: self.col + 1
});
}
}
// Capture straight forward
if (newRow >= 0 && newRow < 8) {
var piece = board[newRow][self.col];
if (piece && piece.color !== self.color) {
moves.push({
row: newRow,
col: self.col
});
}
}
// Initial two-square diagonal move
if (!self.hasMoved) {
newRow = self.row + direction * 2;
if (newRow >= 0 && newRow < 8) {
if (self.col > 1 && !board[newRow][self.col - 2]) {
moves.push({
row: newRow,
col: self.col - 2
});
}
if (self.col < 6 && !board[newRow][self.col + 2]) {
moves.push({
row: newRow,
col: self.col + 2
});
}
}
}
} else if (self.pieceType === 'rook') {
// Rooks move diagonally (like bishops)
var directions = [[-1, -1], [-1, 1], [1, -1], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
for (var i = 1; i < 8; i++) {
var newRow = self.row + dir[0] * i;
var newCol = self.col + dir[1] * i;
if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
var piece = board[newRow][newCol];
if (!piece) {
moves.push({
row: newRow,
col: newCol
});
} else {
if (piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
}
}
} else if (self.pieceType === 'bishop') {
// Bishops move like knights (L-shaped)
var knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]];
for (var k = 0; k < knightMoves.length; k++) {
var move = knightMoves[k];
var newRow = self.row + move[0];
var newCol = self.col + move[1];
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8) {
var piece = board[newRow][newCol];
if (!piece || piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else if (self.pieceType === 'knight') {
// Knights move straight (like rooks)
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
for (var i = 1; i < 8; i++) {
var newRow = self.row + dir[0] * i;
var newCol = self.col + dir[1] * i;
if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
var piece = board[newRow][newCol];
if (!piece) {
moves.push({
row: newRow,
col: newCol
});
} else {
if (piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
}
}
} else if (self.pieceType === 'queen') {
// Queen moves both diagonally and straight
var directions = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
for (var i = 1; i < 8; i++) {
var newRow = self.row + dir[0] * i;
var newCol = self.col + dir[1] * i;
if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
var piece = board[newRow][newCol];
if (!piece) {
moves.push({
row: newRow,
col: newCol
});
} else {
if (piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
}
}
} else if (self.pieceType === 'king') {
// King moves one square in any direction
var directions = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
var newRow = self.row + dir[0];
var newCol = self.col + dir[1];
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8) {
var piece = board[newRow][newCol];
if (!piece || piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
return moves;
};
return self;
});
var ChessSquare = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.isLight = (row + col) % 2 === 0;
self.background = self.attachAsset(self.isLight ? 'lightSquare' : 'darkSquare', {
anchorX: 0.5,
anchorY: 0.5
});
self.highlight = self.attachAsset('highlight', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.validMoveIndicator = self.attachAsset('validMove', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.selectedIndicator = self.attachAsset('selectedPiece', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.setHighlight = function (show) {
self.highlight.alpha = show ? 0.3 : 0;
};
self.setValidMove = function (show) {
self.validMoveIndicator.alpha = show ? 0.5 : 0;
};
self.setSelected = function (show) {
self.selectedIndicator.alpha = show ? 0.4 : 0;
};
self.down = function (x, y, obj) {
handleSquareClick(self.row, self.col);
};
return self;
});
var PromotionDialog = Container.expand(function (color, onSelect) {
var self = Container.call(this);
self.color = color;
self.onSelect = onSelect;
// Background
self.background = self.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5
});
// Title text
self.titleText = new Text2('Choose promotion piece:', {
size: 60,
fill: 0xFFFFFF
});
self.titleText.anchor.set(0.5, 0.5);
self.titleText.x = 0;
self.titleText.y = -120;
self.addChild(self.titleText);
// Promotion pieces
var pieceTypes = ['queen', 'rook', 'bishop', 'knight'];
var buttons = [];
for (var i = 0; i < pieceTypes.length; i++) {
var pieceType = pieceTypes[i];
var button = new Container();
// Button background
var buttonBg = button.attachAsset('promotionButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Piece image
var assetName = color + pieceType.charAt(0).toUpperCase() + pieceType.slice(1);
var pieceImage = button.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
button.x = (i - 1.5) * 180;
button.y = 0;
button.pieceType = pieceType;
// Create closure to capture the correct pieceType for each button
button.down = function (capturedPieceType) {
return function (x, y, obj) {
self.onSelect(capturedPieceType);
};
}(pieceType);
buttons.push(button);
self.addChild(button);
}
// Position dialog at center of screen
self.x = 1024;
self.y = 1366;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
// White pieces
// Black pieces
var boardOffsetX = 224;
var boardOffsetY = 566;
var board = [];
var squares = [];
var pieces = [];
var selectedPiece = null;
var validMoves = [];
var currentPlayer = 'white';
var gameOver = false;
var promotionDialog = null;
var pendingPromotion = null;
var playerPerspective = 'white'; // 'white' or 'black' - determines board orientation
var gameMode = 'menu'; // 'menu', 'computer', 'online'
var isPlayerTurn = true;
var aiColor = 'black';
var mainMenu = null;
var whiteTimeRemaining = 600; // 10 minutes in seconds
var blackTimeRemaining = 600; // 10 minutes in seconds
var gameTimer = null;
var whiteTimerText = null;
var blackTimerText = null;
var lastTimerUpdate = 0;
function showHowToPlay() {
var howToPlayContainer = new Container();
game.addChild(howToPlayContainer);
// Background
var background = howToPlayContainer.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 3.0,
tint: 0xFFA500
});
howToPlayContainer.x = 1024;
howToPlayContainer.y = 1366;
// Title
var titleText = new Text2('How to Play Chess', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -500;
howToPlayContainer.addChild(titleText);
// Piece explanations
var pieceExplanations = [{
piece: 'Pawn',
description: 'Moves diagonally forward, captures straight forward'
}, {
piece: 'Rook',
description: 'Moves diagonally in all directions'
}, {
piece: 'Bishop',
description: 'Moves in L-shape (like a knight)'
}, {
piece: 'Knight',
description: 'Moves straight (horizontal/vertical)'
}, {
piece: 'Queen',
description: 'Moves in all directions (diagonal + straight)'
}, {
piece: 'King',
description: 'Moves one square in any direction'
}];
for (var i = 0; i < pieceExplanations.length; i++) {
var explanation = pieceExplanations[i];
// Piece name
var pieceNameText = new Text2(explanation.piece + ':', {
size: 50,
fill: 0xFFD700
});
pieceNameText.anchor.set(0, 0.5);
pieceNameText.x = -400;
pieceNameText.y = -350 + i * 120;
howToPlayContainer.addChild(pieceNameText);
// Piece description
var descriptionText = new Text2(explanation.description, {
size: 40,
fill: 0xFFFFFF
});
descriptionText.anchor.set(0, 0.5);
descriptionText.x = -400;
descriptionText.y = -320 + i * 120;
howToPlayContainer.addChild(descriptionText);
}
// Additional rules
var rulesText = new Text2('Special Rules:', {
size: 50,
fill: 0xFFD700
});
rulesText.anchor.set(0, 0.5);
rulesText.x = -400;
rulesText.y = 400;
howToPlayContainer.addChild(rulesText);
var promotionText = new Text2('• Pawns promote to Queen when reaching end', {
size: 40,
fill: 0xFFFFFF
});
promotionText.anchor.set(0, 0.5);
promotionText.x = -400;
promotionText.y = 450;
howToPlayContainer.addChild(promotionText);
var checkText = new Text2('• Protect your King from check/checkmate', {
size: 40,
fill: 0xFFFFFF
});
checkText.anchor.set(0, 0.5);
checkText.x = -400;
checkText.y = 500;
howToPlayContainer.addChild(checkText);
// Back button
var backButton = new Container();
var backBg = backButton.attachAsset('promotionButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.8
});
var backText = new Text2('Back', {
size: 60,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.x = 0;
backButton.y = 700;
backButton.down = function (x, y, obj) {
howToPlayContainer.destroy();
};
howToPlayContainer.addChild(backButton);
}
function createMainMenu() {
mainMenu = new Container();
game.addChild(mainMenu);
// Title
var titleText = new Text2('FakeChess', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
mainMenu.addChild(titleText);
// Computer button
var computerButton = new Container();
var computerBg = computerButton.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4
});
var computerText = new Text2('Computer', {
size: 80,
fill: 0xFFFFFF
});
computerText.anchor.set(0.5, 0.5);
computerButton.addChild(computerText);
computerButton.x = 1024;
computerButton.y = 900;
computerButton.down = function (x, y, obj) {
startComputerGame();
};
mainMenu.addChild(computerButton);
// Online button
var onlineButton = new Container();
var onlineBg = onlineButton.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4
});
var onlineText = new Text2('Online', {
size: 80,
fill: 0xFFFFFF
});
onlineText.anchor.set(0.5, 0.5);
onlineButton.addChild(onlineText);
onlineButton.x = 1024;
onlineButton.y = 1200;
onlineButton.down = function (x, y, obj) {
startOnlineGame();
};
mainMenu.addChild(onlineButton);
// How to play button
var howToPlayButton = new Container();
var howToPlayBg = howToPlayButton.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4
});
var howToPlayText = new Text2('How to play?', {
size: 80,
fill: 0xFFFFFF
});
howToPlayText.anchor.set(0.5, 0.5);
howToPlayButton.addChild(howToPlayText);
howToPlayButton.x = 1024;
howToPlayButton.y = 1500;
howToPlayButton.down = function (x, y, obj) {
showHowToPlay();
};
mainMenu.addChild(howToPlayButton);
}
function startComputerGame() {
gameMode = 'computer';
// Randomly assign player color
var playerColor = Math.random() < 0.5 ? 'white' : 'black';
playerPerspective = playerColor;
aiColor = playerColor === 'white' ? 'black' : 'white';
isPlayerTurn = currentPlayer === playerColor;
// Hide menu and initialize game
mainMenu.destroy();
mainMenu = null;
initializeBoard();
initializePieces();
addCoordinateLabels();
createTurnIndicator();
// Play game start sound
LK.getSound('gameStart').play();
// If AI should start (AI is white), make first move
if (aiColor === 'white') {
LK.setTimeout(function () {
makeAIMove();
}, 800);
}
}
function startOnlineGame() {
gameMode = 'online';
// Reset timers
whiteTimeRemaining = 600;
blackTimeRemaining = 600;
playerPerspective = 'white';
mainMenu.destroy();
mainMenu = null;
initializeBoard();
initializePieces();
addCoordinateLabels();
createTurnIndicator();
createTimerDisplay();
startGameTimer();
// Play game start sound
LK.getSound('gameStart').play();
}
function initializeBoard() {
// Initialize board
for (var row = 0; row < 8; row++) {
board[row] = [];
squares[row] = [];
for (var col = 0; col < 8; col++) {
board[row][col] = null;
var square = new ChessSquare(row, col);
if (playerPerspective === 'black') {
// Flip the board for black player perspective
square.x = boardOffsetX + (7 - col) * 200 + 100;
square.y = boardOffsetY + (7 - row) * 200 + 100;
} else {
square.x = boardOffsetX + col * 200 + 100;
square.y = boardOffsetY + row * 200 + 100;
}
squares[row][col] = square;
game.addChild(square);
}
}
}
// Show main menu on startup
createMainMenu();
// Initialize pieces
function initializePieces() {
// White pieces
var whitePieces = [{
type: 'rook',
positions: [[7, 0], [7, 7]]
}, {
type: 'knight',
positions: [[7, 1], [7, 6]]
}, {
type: 'bishop',
positions: [[7, 2], [7, 5]]
}, {
type: 'queen',
positions: [[7, 3]]
}, {
type: 'king',
positions: [[7, 4]]
}, {
type: 'pawn',
positions: [[6, 0], [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [6, 6], [6, 7]]
}];
// Black pieces
var blackPieces = [{
type: 'rook',
positions: [[0, 0], [0, 7]]
}, {
type: 'knight',
positions: [[0, 1], [0, 6]]
}, {
type: 'bishop',
positions: [[0, 2], [0, 5]]
}, {
type: 'queen',
positions: [[0, 3]]
}, {
type: 'king',
positions: [[0, 4]]
}, {
type: 'pawn',
positions: [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7]]
}];
// Create white pieces
for (var i = 0; i < whitePieces.length; i++) {
var pieceData = whitePieces[i];
for (var j = 0; j < pieceData.positions.length; j++) {
var pos = pieceData.positions[j];
var piece = new ChessPiece(pieceData.type, 'white', pos[0], pos[1]);
piece.setPosition(pos[0], pos[1]);
board[pos[0]][pos[1]] = piece;
pieces.push(piece);
game.addChild(piece);
}
}
// Create black pieces
for (var i = 0; i < blackPieces.length; i++) {
var pieceData = blackPieces[i];
for (var j = 0; j < pieceData.positions.length; j++) {
var pos = pieceData.positions[j];
var piece = new ChessPiece(pieceData.type, 'black', pos[0], pos[1]);
piece.setPosition(pos[0], pos[1]);
board[pos[0]][pos[1]] = piece;
pieces.push(piece);
game.addChild(piece);
}
}
}
function clearHighlights() {
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
squares[row][col].setHighlight(false);
squares[row][col].setValidMove(false);
squares[row][col].setSelected(false);
}
}
}
function showValidMoves(piece) {
var allMoves = piece.getValidMoves();
validMoves = [];
// Filter out moves that would leave the king in check
for (var i = 0; i < allMoves.length; i++) {
var move = allMoves[i];
if (isValidMove(piece, move.row, move.col)) {
validMoves.push(move);
squares[move.row][move.col].setValidMove(true);
}
}
}
function handleSquareClick(row, col) {
if (gameOver) return;
if (gameMode === 'computer' && !isPlayerTurn) return; // Don't allow moves during AI turn
if (gameMode === 'menu') return; // Don't allow moves in menu
var clickedPiece = board[row][col];
if (selectedPiece) {
// Check if clicked square is a valid move
var validMove = false;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === row && validMoves[i].col === col) {
validMove = true;
break;
}
}
if (validMove) {
// Make the move
movePiece(selectedPiece, row, col);
selectedPiece = null;
clearHighlights();
validMoves = [];
// Only switch players and check game over if not promoting
if (!pendingPromotion) {
// Switch players
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
if (gameMode === 'computer') {
isPlayerTurn = currentPlayer !== aiColor;
}
// Update timer display for online mode
if (gameMode === 'online') {
updateTimerDisplay();
lastTimerUpdate = Date.now();
}
// Check for game over conditions
checkGameOver();
if (gameMode === 'computer' && !isPlayerTurn) {
// AI turn - make move after short delay
LK.setTimeout(function () {
makeAIMove();
}, 800);
}
}
} else if (clickedPiece && clickedPiece.color === currentPlayer) {
// Select new piece
clearHighlights();
selectedPiece = clickedPiece;
squares[row][col].setSelected(true);
showValidMoves(selectedPiece);
} else {
// Deselect
selectedPiece = null;
clearHighlights();
validMoves = [];
}
} else if (clickedPiece && clickedPiece.color === currentPlayer) {
// Select piece
selectedPiece = clickedPiece;
squares[row][col].setSelected(true);
showValidMoves(selectedPiece);
}
}
function movePiece(piece, toRow, toCol) {
var capturedPiece = board[toRow][toCol];
// Remove piece from old position
board[piece.row][piece.col] = null;
// Place piece in new position
board[toRow][toCol] = piece;
piece.setPosition(toRow, toCol);
piece.hasMoved = true;
// Handle captured piece
if (capturedPiece) {
capturedPiece.destroy();
for (var i = pieces.length - 1; i >= 0; i--) {
if (pieces[i] === capturedPiece) {
pieces.splice(i, 1);
break;
}
}
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Animate piece movement
tween(piece, {
x: piece.x,
y: piece.y
}, {
duration: 300
});
// Check for pawn promotion
if (piece.pieceType === 'pawn') {
var promotionRow = piece.color === 'white' ? 0 : 7;
if (toRow === promotionRow) {
// Check if it's AI's turn in computer mode
if (gameMode === 'computer' && piece.color === aiColor) {
// AI automatically promotes to queen
handleAIPromotion(piece, toRow, toCol, 'queen');
return; // Don't switch players yet
} else {
// Store promotion details for player
pendingPromotion = {
piece: piece,
row: toRow,
col: toCol
};
// Show promotion dialog
showPromotionDialog(piece.color);
return; // Don't switch players yet
}
}
}
}
function isKingInCheck(kingColor) {
// Find the king
var king = null;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'king' && piece.color === kingColor) {
king = piece;
break;
}
}
if (!king) return false;
// Check if any enemy piece can attack the king
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.color !== kingColor) {
var moves = piece.getValidMoves();
for (var j = 0; j < moves.length; j++) {
if (moves[j].row === king.row && moves[j].col === king.col) {
return true;
}
}
}
}
return false;
}
function isValidMove(piece, toRow, toCol) {
// Save current state
var originalRow = piece.row;
var originalCol = piece.col;
var capturedPiece = board[toRow][toCol];
// Temporarily remove captured piece from pieces array for accurate check detection
var capturedPieceIndex = -1;
if (capturedPiece) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === capturedPiece) {
capturedPieceIndex = i;
pieces.splice(i, 1);
break;
}
}
}
// Simulate the move
board[originalRow][originalCol] = null;
board[toRow][toCol] = piece;
piece.row = toRow;
piece.col = toCol;
// Check if this move leaves the king in check
var kingInCheck = isKingInCheck(piece.color);
// Restore original state
board[originalRow][originalCol] = piece;
board[toRow][toCol] = capturedPiece;
piece.row = originalRow;
piece.col = originalCol;
// Restore captured piece to pieces array
if (capturedPiece && capturedPieceIndex !== -1) {
pieces.splice(capturedPieceIndex, 0, capturedPiece);
}
// Debug: Log when moves are being blocked
if (kingInCheck && capturedPiece) {
console.log("Move blocked: " + piece.pieceType + " cannot capture " + capturedPiece.pieceType + " - king still in check");
}
return !kingInCheck;
}
function getValidMovesForPlayer(playerColor) {
var allMoves = [];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.color === playerColor) {
var moves = piece.getValidMoves();
for (var j = 0; j < moves.length; j++) {
if (isValidMove(piece, moves[j].row, moves[j].col)) {
allMoves.push({
piece: piece,
row: moves[j].row,
col: moves[j].col
});
}
}
}
}
return allMoves;
}
function makeAIMove() {
if (gameMode !== 'computer' || currentPlayer !== aiColor || gameOver) return;
var validMoves = getValidMovesForPlayer(aiColor);
if (validMoves.length === 0) return;
// Evaluate and score all possible moves
var bestMoves = [];
var bestScore = -1000;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var score = evaluateMove(move);
if (score > bestScore) {
bestScore = score;
bestMoves = [move];
} else if (score === bestScore) {
bestMoves.push(move);
}
}
// Pick randomly from the best moves
var selectedMove = bestMoves[Math.floor(Math.random() * bestMoves.length)];
movePiece(selectedMove.piece, selectedMove.row, selectedMove.col);
// Clear selection and highlights
selectedPiece = null;
clearHighlights();
// Switch players if not promoting
if (!pendingPromotion) {
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
isPlayerTurn = currentPlayer !== aiColor;
checkGameOver();
}
}
function evaluateMove(move) {
var score = 0;
var targetPiece = board[move.row][move.col];
var movingPiece = move.piece;
// Prioritize captures - assign values to different pieces
if (targetPiece) {
var pieceValues = {
'pawn': 10,
'knight': 30,
'bishop': 30,
'rook': 50,
'queen': 90,
'king': 1000
};
score += pieceValues[targetPiece.pieceType] || 0;
}
// Bonus for moving towards center
var centerDistance = Math.abs(move.row - 3.5) + Math.abs(move.col - 3.5);
score += (7 - centerDistance) * 2;
// Bonus for advancing pawns
if (movingPiece.pieceType === 'pawn') {
if (aiColor === 'white') {
score += (7 - move.row) * 3; // White pawns advance by decreasing row
} else {
score += move.row * 3; // Black pawns advance by increasing row
}
}
// Bonus for developing pieces early
if (!movingPiece.hasMoved) {
if (movingPiece.pieceType === 'knight' || movingPiece.pieceType === 'bishop') {
score += 15;
}
}
// Small penalty for moving the same piece multiple times early
if (movingPiece.hasMoved && movingPiece.pieceType !== 'pawn') {
score -= 2;
}
// Bonus for protecting valuable pieces
var protectedPieces = 0;
var attackingMoves = movingPiece.getValidMoves();
for (var i = 0; i < attackingMoves.length; i++) {
var attackMove = attackingMoves[i];
var protectedPiece = board[attackMove.row][attackMove.col];
if (protectedPiece && protectedPiece.color === aiColor) {
protectedPieces++;
}
}
score += protectedPieces * 5;
return score;
}
function checkGameOver() {
// Find kings
var whiteKing = null;
var blackKing = null;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'king') {
if (piece.color === 'white') {
whiteKing = piece;
} else {
blackKing = piece;
}
}
}
// Check if either king is captured (shouldn't happen in proper chess)
if (!whiteKing) {
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
return;
} else if (!blackKing) {
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
return;
}
// Check for check and checkmate
var currentPlayerInCheck = isKingInCheck(currentPlayer);
var validMoves = getValidMovesForPlayer(currentPlayer);
if (currentPlayerInCheck) {
if (validMoves.length === 0) {
// Checkmate - the current player is in check and has no valid moves
gameOver = true;
LK.getSound('checkmate').play();
// In computer mode, determine win/loss based on player perspective
if (gameMode === 'computer') {
if (currentPlayer === playerPerspective) {
// Player is checkmated - AI wins
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
} else {
// AI is checkmated - Player wins
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
}
} else {
// Online mode - show result based on color that won
var winner = currentPlayer === 'white' ? 'black' : 'white'; // The winner is the opposite of who is checkmated
LK.setTimeout(function () {
if (winner === 'black') {
// Black won
var blackWonText = new Text2('Black Won!', {
size: 120,
fill: 0xFFFFFF
});
blackWonText.anchor.set(0.5, 0.5);
blackWonText.x = 1024;
blackWonText.y = 1366;
game.addChild(blackWonText);
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
// White won
var whiteWonText = new Text2('White Won!', {
size: 120,
fill: 0xFFFFFF
});
whiteWonText.anchor.set(0.5, 0.5);
whiteWonText.x = 1024;
whiteWonText.y = 1366;
game.addChild(whiteWonText);
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
}
}, 1000);
}
} else {
// Check warning - player is in check but has moves
LK.effects.flashScreen(0xff0000, 500);
LK.getSound('check').play();
}
} else if (validMoves.length === 0) {
// Stalemate - no check but no valid moves
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
}
function showPromotionDialog(color) {
if (promotionDialog) {
promotionDialog.destroy();
}
promotionDialog = new PromotionDialog(color, function (selectedPiece) {
handlePromotion(selectedPiece);
});
game.addChild(promotionDialog);
}
function handlePromotion(newPieceType) {
if (!pendingPromotion) return;
var oldPiece = pendingPromotion.piece;
var row = pendingPromotion.row;
var col = pendingPromotion.col;
// Remove old pawn
oldPiece.destroy();
for (var i = pieces.length - 1; i >= 0; i--) {
if (pieces[i] === oldPiece) {
pieces.splice(i, 1);
break;
}
}
board[row][col] = null;
// Create new piece
var newPiece = new ChessPiece(newPieceType, oldPiece.color, row, col);
newPiece.setPosition(row, col);
newPiece.hasMoved = true;
board[row][col] = newPiece;
pieces.push(newPiece);
game.addChild(newPiece);
// Clean up
promotionDialog.destroy();
promotionDialog = null;
pendingPromotion = null;
// Switch players
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
// Update timer display for online mode
if (gameMode === 'online') {
updateTimerDisplay();
lastTimerUpdate = Date.now();
}
// Check for game over conditions
checkGameOver();
}
function handleAIPromotion(oldPiece, row, col, newPieceType) {
// Remove old pawn
oldPiece.destroy();
for (var i = pieces.length - 1; i >= 0; i--) {
if (pieces[i] === oldPiece) {
pieces.splice(i, 1);
break;
}
}
board[row][col] = null;
// Create new piece
var newPiece = new ChessPiece(newPieceType, oldPiece.color, row, col);
newPiece.setPosition(row, col);
newPiece.hasMoved = true;
board[row][col] = newPiece;
pieces.push(newPiece);
game.addChild(newPiece);
// Switch players
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
if (gameMode === 'computer') {
isPlayerTurn = currentPlayer !== aiColor;
}
// Check for game over conditions
checkGameOver();
// Continue game flow after promotion - ensure AI can continue playing
if (gameMode === 'computer' && currentPlayer === aiColor && !gameOver) {
// If it's AI's turn after promotion, schedule another AI move
LK.setTimeout(function () {
makeAIMove();
}, 800);
}
}
function setPlayerPerspective(perspective) {
playerPerspective = perspective;
// Clear existing board and pieces
for (var i = pieces.length - 1; i >= 0; i--) {
pieces[i].destroy();
}
pieces = [];
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
board[row][col] = null;
squares[row][col].destroy();
}
}
// Clear coordinate labels
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Text2 && child !== turnText) {
child.destroy();
}
}
// Reinitialize board
for (var row = 0; row < 8; row++) {
board[row] = [];
squares[row] = [];
for (var col = 0; col < 8; col++) {
board[row][col] = null;
var square = new ChessSquare(row, col);
if (playerPerspective === 'black') {
square.x = boardOffsetX + (7 - col) * 200 + 100;
square.y = boardOffsetY + (7 - row) * 200 + 100;
} else {
square.x = boardOffsetX + col * 200 + 100;
square.y = boardOffsetY + row * 200 + 100;
}
squares[row][col] = square;
game.addChild(square);
}
}
// Reinitialize pieces and coordinates
initializePieces();
addCoordinateLabels();
}
function addCoordinateLabels() {
// Add numbers 1-8 on the left side (always from white's perspective)
for (var i = 0; i < 8; i++) {
var rankNumber, rankY;
if (playerPerspective === 'black') {
rankNumber = i + 1;
rankY = boardOffsetY + i * 200 + 100;
} else {
rankNumber = 8 - i;
rankY = boardOffsetY + i * 200 + 100;
}
var rankText = new Text2(rankNumber.toString(), {
size: 40,
fill: 0xFFFFFF
});
rankText.anchor.set(0.5, 0.5);
rankText.x = boardOffsetX - 60;
rankText.y = rankY;
game.addChild(rankText);
}
// Add letters a-h (always from white's perspective)
var files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
for (var i = 0; i < 8; i++) {
var fileLabel, fileX;
if (playerPerspective === 'black') {
fileLabel = files[7 - i];
fileX = boardOffsetX + i * 200 + 100;
} else {
fileLabel = files[i];
fileX = boardOffsetX + i * 200 + 100;
}
var fileText = new Text2(fileLabel, {
size: 40,
fill: 0xFFFFFF
});
fileText.anchor.set(0.5, 0.5);
fileText.x = fileX;
fileText.y = boardOffsetY + 8 * 200 + 60;
game.addChild(fileText);
}
}
function createTurnIndicator() {
// Add turn indicator
turnText = new Text2('White to move', {
size: 80,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.x = 1024;
turnText.y = 200;
game.addChild(turnText);
}
function createTimerDisplay() {
if (gameMode !== 'online') return;
// White timer
whiteTimerText = new Text2(formatTime(whiteTimeRemaining), {
size: 60,
fill: 0xFFFFFF
});
whiteTimerText.anchor.set(0.5, 0.5);
whiteTimerText.x = boardOffsetX + 1600 - 80;
whiteTimerText.y = boardOffsetY + 1600 + 120;
game.addChild(whiteTimerText);
// Black timer
blackTimerText = new Text2(formatTime(blackTimeRemaining), {
size: 60,
fill: 0xFFFFFF
});
blackTimerText.anchor.set(0.5, 0.5);
blackTimerText.x = boardOffsetX + 1600 - 80;
blackTimerText.y = boardOffsetY - 60;
game.addChild(blackTimerText);
}
function formatTime(seconds) {
var minutes = Math.floor(seconds / 60);
var remainingSeconds = seconds % 60;
return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds;
}
function startGameTimer() {
if (gameMode !== 'online') return;
lastTimerUpdate = Date.now();
gameTimer = LK.setInterval(function () {
var currentTime = Date.now();
var deltaTime = Math.floor((currentTime - lastTimerUpdate) / 1000);
if (deltaTime >= 1) {
if (currentPlayer === 'white') {
whiteTimeRemaining -= deltaTime;
if (whiteTimeRemaining <= 0) {
whiteTimeRemaining = 0;
handleTimeOut('white');
return;
}
} else {
blackTimeRemaining -= deltaTime;
if (blackTimeRemaining <= 0) {
blackTimeRemaining = 0;
handleTimeOut('black');
return;
}
}
updateTimerDisplay();
lastTimerUpdate = currentTime;
}
}, 100);
}
function updateTimerDisplay() {
if (gameMode !== 'online') return;
if (whiteTimerText) {
whiteTimerText.setText(formatTime(whiteTimeRemaining));
// Highlight current player's timer
if (currentPlayer === 'white') {
whiteTimerText.tint = 0x00ff00;
blackTimerText.tint = 0xffffff;
} else {
whiteTimerText.tint = 0xffffff;
blackTimerText.tint = 0x00ff00;
}
}
if (blackTimerText) {
blackTimerText.setText(formatTime(blackTimeRemaining));
}
}
function handleTimeOut(timedOutPlayer) {
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
if (timedOutPlayer === 'white') {
LK.showGameOver();
} else {
LK.showYouWin();
}
}, 1000);
}
function stopGameTimer() {
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
}
// Game starts with main menu - no immediate initialization
game.update = function () {
// Only update turn indicator if not in menu and turnText exists
if (gameMode !== 'menu' && turnText && !gameOver) {
var checkText = '';
var currentPlayerInCheck = isKingInCheck(currentPlayer);
var validMoves = getValidMovesForPlayer(currentPlayer);
if (currentPlayerInCheck) {
if (validMoves.length === 0) {
checkText = ' - CHECKMATE!';
} else {
checkText = ' - CHECK!';
}
}
var turnDisplay = '';
if (gameMode === 'computer') {
if (currentPlayer === playerPerspective) {
turnDisplay = 'Your turn';
} else {
turnDisplay = 'AI thinking...';
}
} else if (gameMode === 'online') {
turnDisplay = currentPlayer === 'white' ? 'White to move' : 'Black to move';
// Add time pressure warning
if (currentPlayer === 'white' && whiteTimeRemaining <= 60) {
turnDisplay += ' (LOW TIME!)';
} else if (currentPlayer === 'black' && blackTimeRemaining <= 60) {
turnDisplay += ' (LOW TIME!)';
}
} else {
turnDisplay = currentPlayer === 'white' ? 'White to move' : 'Black to move';
}
turnText.setText(turnDisplay + checkText);
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ChessPiece = Container.expand(function (type, color, row, col) {
var self = Container.call(this);
self.pieceType = type;
self.color = color;
self.row = row;
self.col = col;
self.hasMoved = false;
var assetName = color + type.charAt(0).toUpperCase() + type.slice(1);
self.graphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.setPosition = function (row, col) {
self.row = row;
self.col = col;
if (playerPerspective === 'black') {
// Flip the board for black player perspective
self.x = boardOffsetX + (7 - col) * 200 + 100;
self.y = boardOffsetY + (7 - row) * 200 + 100;
} else {
self.x = boardOffsetX + col * 200 + 100;
self.y = boardOffsetY + row * 200 + 100;
}
};
self.getValidMoves = function () {
var moves = [];
if (self.pieceType === 'pawn') {
// Pawns move diagonally forward, capture straight forward
var direction = self.color === 'white' ? -1 : 1;
var newRow = self.row + direction;
// Diagonal movement (normal pawn movement)
if (newRow >= 0 && newRow < 8) {
if (self.col > 0 && !board[newRow][self.col - 1]) {
moves.push({
row: newRow,
col: self.col - 1
});
}
if (self.col < 7 && !board[newRow][self.col + 1]) {
moves.push({
row: newRow,
col: self.col + 1
});
}
}
// Capture straight forward
if (newRow >= 0 && newRow < 8) {
var piece = board[newRow][self.col];
if (piece && piece.color !== self.color) {
moves.push({
row: newRow,
col: self.col
});
}
}
// Initial two-square diagonal move
if (!self.hasMoved) {
newRow = self.row + direction * 2;
if (newRow >= 0 && newRow < 8) {
if (self.col > 1 && !board[newRow][self.col - 2]) {
moves.push({
row: newRow,
col: self.col - 2
});
}
if (self.col < 6 && !board[newRow][self.col + 2]) {
moves.push({
row: newRow,
col: self.col + 2
});
}
}
}
} else if (self.pieceType === 'rook') {
// Rooks move diagonally (like bishops)
var directions = [[-1, -1], [-1, 1], [1, -1], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
for (var i = 1; i < 8; i++) {
var newRow = self.row + dir[0] * i;
var newCol = self.col + dir[1] * i;
if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
var piece = board[newRow][newCol];
if (!piece) {
moves.push({
row: newRow,
col: newCol
});
} else {
if (piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
}
}
} else if (self.pieceType === 'bishop') {
// Bishops move like knights (L-shaped)
var knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]];
for (var k = 0; k < knightMoves.length; k++) {
var move = knightMoves[k];
var newRow = self.row + move[0];
var newCol = self.col + move[1];
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8) {
var piece = board[newRow][newCol];
if (!piece || piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else if (self.pieceType === 'knight') {
// Knights move straight (like rooks)
var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
for (var i = 1; i < 8; i++) {
var newRow = self.row + dir[0] * i;
var newCol = self.col + dir[1] * i;
if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
var piece = board[newRow][newCol];
if (!piece) {
moves.push({
row: newRow,
col: newCol
});
} else {
if (piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
}
}
} else if (self.pieceType === 'queen') {
// Queen moves both diagonally and straight
var directions = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
for (var i = 1; i < 8; i++) {
var newRow = self.row + dir[0] * i;
var newCol = self.col + dir[1] * i;
if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
var piece = board[newRow][newCol];
if (!piece) {
moves.push({
row: newRow,
col: newCol
});
} else {
if (piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
}
}
} else if (self.pieceType === 'king') {
// King moves one square in any direction
var directions = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]];
for (var d = 0; d < directions.length; d++) {
var dir = directions[d];
var newRow = self.row + dir[0];
var newCol = self.col + dir[1];
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8) {
var piece = board[newRow][newCol];
if (!piece || piece.color !== self.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
return moves;
};
return self;
});
var ChessSquare = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.isLight = (row + col) % 2 === 0;
self.background = self.attachAsset(self.isLight ? 'lightSquare' : 'darkSquare', {
anchorX: 0.5,
anchorY: 0.5
});
self.highlight = self.attachAsset('highlight', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.validMoveIndicator = self.attachAsset('validMove', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.selectedIndicator = self.attachAsset('selectedPiece', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.setHighlight = function (show) {
self.highlight.alpha = show ? 0.3 : 0;
};
self.setValidMove = function (show) {
self.validMoveIndicator.alpha = show ? 0.5 : 0;
};
self.setSelected = function (show) {
self.selectedIndicator.alpha = show ? 0.4 : 0;
};
self.down = function (x, y, obj) {
handleSquareClick(self.row, self.col);
};
return self;
});
var PromotionDialog = Container.expand(function (color, onSelect) {
var self = Container.call(this);
self.color = color;
self.onSelect = onSelect;
// Background
self.background = self.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5
});
// Title text
self.titleText = new Text2('Choose promotion piece:', {
size: 60,
fill: 0xFFFFFF
});
self.titleText.anchor.set(0.5, 0.5);
self.titleText.x = 0;
self.titleText.y = -120;
self.addChild(self.titleText);
// Promotion pieces
var pieceTypes = ['queen', 'rook', 'bishop', 'knight'];
var buttons = [];
for (var i = 0; i < pieceTypes.length; i++) {
var pieceType = pieceTypes[i];
var button = new Container();
// Button background
var buttonBg = button.attachAsset('promotionButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Piece image
var assetName = color + pieceType.charAt(0).toUpperCase() + pieceType.slice(1);
var pieceImage = button.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
button.x = (i - 1.5) * 180;
button.y = 0;
button.pieceType = pieceType;
// Create closure to capture the correct pieceType for each button
button.down = function (capturedPieceType) {
return function (x, y, obj) {
self.onSelect(capturedPieceType);
};
}(pieceType);
buttons.push(button);
self.addChild(button);
}
// Position dialog at center of screen
self.x = 1024;
self.y = 1366;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
// White pieces
// Black pieces
var boardOffsetX = 224;
var boardOffsetY = 566;
var board = [];
var squares = [];
var pieces = [];
var selectedPiece = null;
var validMoves = [];
var currentPlayer = 'white';
var gameOver = false;
var promotionDialog = null;
var pendingPromotion = null;
var playerPerspective = 'white'; // 'white' or 'black' - determines board orientation
var gameMode = 'menu'; // 'menu', 'computer', 'online'
var isPlayerTurn = true;
var aiColor = 'black';
var mainMenu = null;
var whiteTimeRemaining = 600; // 10 minutes in seconds
var blackTimeRemaining = 600; // 10 minutes in seconds
var gameTimer = null;
var whiteTimerText = null;
var blackTimerText = null;
var lastTimerUpdate = 0;
function showHowToPlay() {
var howToPlayContainer = new Container();
game.addChild(howToPlayContainer);
// Background
var background = howToPlayContainer.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 3.0,
tint: 0xFFA500
});
howToPlayContainer.x = 1024;
howToPlayContainer.y = 1366;
// Title
var titleText = new Text2('How to Play Chess', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -500;
howToPlayContainer.addChild(titleText);
// Piece explanations
var pieceExplanations = [{
piece: 'Pawn',
description: 'Moves diagonally forward, captures straight forward'
}, {
piece: 'Rook',
description: 'Moves diagonally in all directions'
}, {
piece: 'Bishop',
description: 'Moves in L-shape (like a knight)'
}, {
piece: 'Knight',
description: 'Moves straight (horizontal/vertical)'
}, {
piece: 'Queen',
description: 'Moves in all directions (diagonal + straight)'
}, {
piece: 'King',
description: 'Moves one square in any direction'
}];
for (var i = 0; i < pieceExplanations.length; i++) {
var explanation = pieceExplanations[i];
// Piece name
var pieceNameText = new Text2(explanation.piece + ':', {
size: 50,
fill: 0xFFD700
});
pieceNameText.anchor.set(0, 0.5);
pieceNameText.x = -400;
pieceNameText.y = -350 + i * 120;
howToPlayContainer.addChild(pieceNameText);
// Piece description
var descriptionText = new Text2(explanation.description, {
size: 40,
fill: 0xFFFFFF
});
descriptionText.anchor.set(0, 0.5);
descriptionText.x = -400;
descriptionText.y = -320 + i * 120;
howToPlayContainer.addChild(descriptionText);
}
// Additional rules
var rulesText = new Text2('Special Rules:', {
size: 50,
fill: 0xFFD700
});
rulesText.anchor.set(0, 0.5);
rulesText.x = -400;
rulesText.y = 400;
howToPlayContainer.addChild(rulesText);
var promotionText = new Text2('• Pawns promote to Queen when reaching end', {
size: 40,
fill: 0xFFFFFF
});
promotionText.anchor.set(0, 0.5);
promotionText.x = -400;
promotionText.y = 450;
howToPlayContainer.addChild(promotionText);
var checkText = new Text2('• Protect your King from check/checkmate', {
size: 40,
fill: 0xFFFFFF
});
checkText.anchor.set(0, 0.5);
checkText.x = -400;
checkText.y = 500;
howToPlayContainer.addChild(checkText);
// Back button
var backButton = new Container();
var backBg = backButton.attachAsset('promotionButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.8
});
var backText = new Text2('Back', {
size: 60,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.x = 0;
backButton.y = 700;
backButton.down = function (x, y, obj) {
howToPlayContainer.destroy();
};
howToPlayContainer.addChild(backButton);
}
function createMainMenu() {
mainMenu = new Container();
game.addChild(mainMenu);
// Title
var titleText = new Text2('FakeChess', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
mainMenu.addChild(titleText);
// Computer button
var computerButton = new Container();
var computerBg = computerButton.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4
});
var computerText = new Text2('Computer', {
size: 80,
fill: 0xFFFFFF
});
computerText.anchor.set(0.5, 0.5);
computerButton.addChild(computerText);
computerButton.x = 1024;
computerButton.y = 900;
computerButton.down = function (x, y, obj) {
startComputerGame();
};
mainMenu.addChild(computerButton);
// Online button
var onlineButton = new Container();
var onlineBg = onlineButton.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4
});
var onlineText = new Text2('Online', {
size: 80,
fill: 0xFFFFFF
});
onlineText.anchor.set(0.5, 0.5);
onlineButton.addChild(onlineText);
onlineButton.x = 1024;
onlineButton.y = 1200;
onlineButton.down = function (x, y, obj) {
startOnlineGame();
};
mainMenu.addChild(onlineButton);
// How to play button
var howToPlayButton = new Container();
var howToPlayBg = howToPlayButton.attachAsset('promotionDialog', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4
});
var howToPlayText = new Text2('How to play?', {
size: 80,
fill: 0xFFFFFF
});
howToPlayText.anchor.set(0.5, 0.5);
howToPlayButton.addChild(howToPlayText);
howToPlayButton.x = 1024;
howToPlayButton.y = 1500;
howToPlayButton.down = function (x, y, obj) {
showHowToPlay();
};
mainMenu.addChild(howToPlayButton);
}
function startComputerGame() {
gameMode = 'computer';
// Randomly assign player color
var playerColor = Math.random() < 0.5 ? 'white' : 'black';
playerPerspective = playerColor;
aiColor = playerColor === 'white' ? 'black' : 'white';
isPlayerTurn = currentPlayer === playerColor;
// Hide menu and initialize game
mainMenu.destroy();
mainMenu = null;
initializeBoard();
initializePieces();
addCoordinateLabels();
createTurnIndicator();
// Play game start sound
LK.getSound('gameStart').play();
// If AI should start (AI is white), make first move
if (aiColor === 'white') {
LK.setTimeout(function () {
makeAIMove();
}, 800);
}
}
function startOnlineGame() {
gameMode = 'online';
// Reset timers
whiteTimeRemaining = 600;
blackTimeRemaining = 600;
playerPerspective = 'white';
mainMenu.destroy();
mainMenu = null;
initializeBoard();
initializePieces();
addCoordinateLabels();
createTurnIndicator();
createTimerDisplay();
startGameTimer();
// Play game start sound
LK.getSound('gameStart').play();
}
function initializeBoard() {
// Initialize board
for (var row = 0; row < 8; row++) {
board[row] = [];
squares[row] = [];
for (var col = 0; col < 8; col++) {
board[row][col] = null;
var square = new ChessSquare(row, col);
if (playerPerspective === 'black') {
// Flip the board for black player perspective
square.x = boardOffsetX + (7 - col) * 200 + 100;
square.y = boardOffsetY + (7 - row) * 200 + 100;
} else {
square.x = boardOffsetX + col * 200 + 100;
square.y = boardOffsetY + row * 200 + 100;
}
squares[row][col] = square;
game.addChild(square);
}
}
}
// Show main menu on startup
createMainMenu();
// Initialize pieces
function initializePieces() {
// White pieces
var whitePieces = [{
type: 'rook',
positions: [[7, 0], [7, 7]]
}, {
type: 'knight',
positions: [[7, 1], [7, 6]]
}, {
type: 'bishop',
positions: [[7, 2], [7, 5]]
}, {
type: 'queen',
positions: [[7, 3]]
}, {
type: 'king',
positions: [[7, 4]]
}, {
type: 'pawn',
positions: [[6, 0], [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [6, 6], [6, 7]]
}];
// Black pieces
var blackPieces = [{
type: 'rook',
positions: [[0, 0], [0, 7]]
}, {
type: 'knight',
positions: [[0, 1], [0, 6]]
}, {
type: 'bishop',
positions: [[0, 2], [0, 5]]
}, {
type: 'queen',
positions: [[0, 3]]
}, {
type: 'king',
positions: [[0, 4]]
}, {
type: 'pawn',
positions: [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7]]
}];
// Create white pieces
for (var i = 0; i < whitePieces.length; i++) {
var pieceData = whitePieces[i];
for (var j = 0; j < pieceData.positions.length; j++) {
var pos = pieceData.positions[j];
var piece = new ChessPiece(pieceData.type, 'white', pos[0], pos[1]);
piece.setPosition(pos[0], pos[1]);
board[pos[0]][pos[1]] = piece;
pieces.push(piece);
game.addChild(piece);
}
}
// Create black pieces
for (var i = 0; i < blackPieces.length; i++) {
var pieceData = blackPieces[i];
for (var j = 0; j < pieceData.positions.length; j++) {
var pos = pieceData.positions[j];
var piece = new ChessPiece(pieceData.type, 'black', pos[0], pos[1]);
piece.setPosition(pos[0], pos[1]);
board[pos[0]][pos[1]] = piece;
pieces.push(piece);
game.addChild(piece);
}
}
}
function clearHighlights() {
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
squares[row][col].setHighlight(false);
squares[row][col].setValidMove(false);
squares[row][col].setSelected(false);
}
}
}
function showValidMoves(piece) {
var allMoves = piece.getValidMoves();
validMoves = [];
// Filter out moves that would leave the king in check
for (var i = 0; i < allMoves.length; i++) {
var move = allMoves[i];
if (isValidMove(piece, move.row, move.col)) {
validMoves.push(move);
squares[move.row][move.col].setValidMove(true);
}
}
}
function handleSquareClick(row, col) {
if (gameOver) return;
if (gameMode === 'computer' && !isPlayerTurn) return; // Don't allow moves during AI turn
if (gameMode === 'menu') return; // Don't allow moves in menu
var clickedPiece = board[row][col];
if (selectedPiece) {
// Check if clicked square is a valid move
var validMove = false;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === row && validMoves[i].col === col) {
validMove = true;
break;
}
}
if (validMove) {
// Make the move
movePiece(selectedPiece, row, col);
selectedPiece = null;
clearHighlights();
validMoves = [];
// Only switch players and check game over if not promoting
if (!pendingPromotion) {
// Switch players
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
if (gameMode === 'computer') {
isPlayerTurn = currentPlayer !== aiColor;
}
// Update timer display for online mode
if (gameMode === 'online') {
updateTimerDisplay();
lastTimerUpdate = Date.now();
}
// Check for game over conditions
checkGameOver();
if (gameMode === 'computer' && !isPlayerTurn) {
// AI turn - make move after short delay
LK.setTimeout(function () {
makeAIMove();
}, 800);
}
}
} else if (clickedPiece && clickedPiece.color === currentPlayer) {
// Select new piece
clearHighlights();
selectedPiece = clickedPiece;
squares[row][col].setSelected(true);
showValidMoves(selectedPiece);
} else {
// Deselect
selectedPiece = null;
clearHighlights();
validMoves = [];
}
} else if (clickedPiece && clickedPiece.color === currentPlayer) {
// Select piece
selectedPiece = clickedPiece;
squares[row][col].setSelected(true);
showValidMoves(selectedPiece);
}
}
function movePiece(piece, toRow, toCol) {
var capturedPiece = board[toRow][toCol];
// Remove piece from old position
board[piece.row][piece.col] = null;
// Place piece in new position
board[toRow][toCol] = piece;
piece.setPosition(toRow, toCol);
piece.hasMoved = true;
// Handle captured piece
if (capturedPiece) {
capturedPiece.destroy();
for (var i = pieces.length - 1; i >= 0; i--) {
if (pieces[i] === capturedPiece) {
pieces.splice(i, 1);
break;
}
}
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Animate piece movement
tween(piece, {
x: piece.x,
y: piece.y
}, {
duration: 300
});
// Check for pawn promotion
if (piece.pieceType === 'pawn') {
var promotionRow = piece.color === 'white' ? 0 : 7;
if (toRow === promotionRow) {
// Check if it's AI's turn in computer mode
if (gameMode === 'computer' && piece.color === aiColor) {
// AI automatically promotes to queen
handleAIPromotion(piece, toRow, toCol, 'queen');
return; // Don't switch players yet
} else {
// Store promotion details for player
pendingPromotion = {
piece: piece,
row: toRow,
col: toCol
};
// Show promotion dialog
showPromotionDialog(piece.color);
return; // Don't switch players yet
}
}
}
}
function isKingInCheck(kingColor) {
// Find the king
var king = null;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'king' && piece.color === kingColor) {
king = piece;
break;
}
}
if (!king) return false;
// Check if any enemy piece can attack the king
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.color !== kingColor) {
var moves = piece.getValidMoves();
for (var j = 0; j < moves.length; j++) {
if (moves[j].row === king.row && moves[j].col === king.col) {
return true;
}
}
}
}
return false;
}
function isValidMove(piece, toRow, toCol) {
// Save current state
var originalRow = piece.row;
var originalCol = piece.col;
var capturedPiece = board[toRow][toCol];
// Temporarily remove captured piece from pieces array for accurate check detection
var capturedPieceIndex = -1;
if (capturedPiece) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === capturedPiece) {
capturedPieceIndex = i;
pieces.splice(i, 1);
break;
}
}
}
// Simulate the move
board[originalRow][originalCol] = null;
board[toRow][toCol] = piece;
piece.row = toRow;
piece.col = toCol;
// Check if this move leaves the king in check
var kingInCheck = isKingInCheck(piece.color);
// Restore original state
board[originalRow][originalCol] = piece;
board[toRow][toCol] = capturedPiece;
piece.row = originalRow;
piece.col = originalCol;
// Restore captured piece to pieces array
if (capturedPiece && capturedPieceIndex !== -1) {
pieces.splice(capturedPieceIndex, 0, capturedPiece);
}
// Debug: Log when moves are being blocked
if (kingInCheck && capturedPiece) {
console.log("Move blocked: " + piece.pieceType + " cannot capture " + capturedPiece.pieceType + " - king still in check");
}
return !kingInCheck;
}
function getValidMovesForPlayer(playerColor) {
var allMoves = [];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.color === playerColor) {
var moves = piece.getValidMoves();
for (var j = 0; j < moves.length; j++) {
if (isValidMove(piece, moves[j].row, moves[j].col)) {
allMoves.push({
piece: piece,
row: moves[j].row,
col: moves[j].col
});
}
}
}
}
return allMoves;
}
function makeAIMove() {
if (gameMode !== 'computer' || currentPlayer !== aiColor || gameOver) return;
var validMoves = getValidMovesForPlayer(aiColor);
if (validMoves.length === 0) return;
// Evaluate and score all possible moves
var bestMoves = [];
var bestScore = -1000;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var score = evaluateMove(move);
if (score > bestScore) {
bestScore = score;
bestMoves = [move];
} else if (score === bestScore) {
bestMoves.push(move);
}
}
// Pick randomly from the best moves
var selectedMove = bestMoves[Math.floor(Math.random() * bestMoves.length)];
movePiece(selectedMove.piece, selectedMove.row, selectedMove.col);
// Clear selection and highlights
selectedPiece = null;
clearHighlights();
// Switch players if not promoting
if (!pendingPromotion) {
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
isPlayerTurn = currentPlayer !== aiColor;
checkGameOver();
}
}
function evaluateMove(move) {
var score = 0;
var targetPiece = board[move.row][move.col];
var movingPiece = move.piece;
// Prioritize captures - assign values to different pieces
if (targetPiece) {
var pieceValues = {
'pawn': 10,
'knight': 30,
'bishop': 30,
'rook': 50,
'queen': 90,
'king': 1000
};
score += pieceValues[targetPiece.pieceType] || 0;
}
// Bonus for moving towards center
var centerDistance = Math.abs(move.row - 3.5) + Math.abs(move.col - 3.5);
score += (7 - centerDistance) * 2;
// Bonus for advancing pawns
if (movingPiece.pieceType === 'pawn') {
if (aiColor === 'white') {
score += (7 - move.row) * 3; // White pawns advance by decreasing row
} else {
score += move.row * 3; // Black pawns advance by increasing row
}
}
// Bonus for developing pieces early
if (!movingPiece.hasMoved) {
if (movingPiece.pieceType === 'knight' || movingPiece.pieceType === 'bishop') {
score += 15;
}
}
// Small penalty for moving the same piece multiple times early
if (movingPiece.hasMoved && movingPiece.pieceType !== 'pawn') {
score -= 2;
}
// Bonus for protecting valuable pieces
var protectedPieces = 0;
var attackingMoves = movingPiece.getValidMoves();
for (var i = 0; i < attackingMoves.length; i++) {
var attackMove = attackingMoves[i];
var protectedPiece = board[attackMove.row][attackMove.col];
if (protectedPiece && protectedPiece.color === aiColor) {
protectedPieces++;
}
}
score += protectedPieces * 5;
return score;
}
function checkGameOver() {
// Find kings
var whiteKing = null;
var blackKing = null;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'king') {
if (piece.color === 'white') {
whiteKing = piece;
} else {
blackKing = piece;
}
}
}
// Check if either king is captured (shouldn't happen in proper chess)
if (!whiteKing) {
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
return;
} else if (!blackKing) {
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
return;
}
// Check for check and checkmate
var currentPlayerInCheck = isKingInCheck(currentPlayer);
var validMoves = getValidMovesForPlayer(currentPlayer);
if (currentPlayerInCheck) {
if (validMoves.length === 0) {
// Checkmate - the current player is in check and has no valid moves
gameOver = true;
LK.getSound('checkmate').play();
// In computer mode, determine win/loss based on player perspective
if (gameMode === 'computer') {
if (currentPlayer === playerPerspective) {
// Player is checkmated - AI wins
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
} else {
// AI is checkmated - Player wins
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
}
} else {
// Online mode - show result based on color that won
var winner = currentPlayer === 'white' ? 'black' : 'white'; // The winner is the opposite of who is checkmated
LK.setTimeout(function () {
if (winner === 'black') {
// Black won
var blackWonText = new Text2('Black Won!', {
size: 120,
fill: 0xFFFFFF
});
blackWonText.anchor.set(0.5, 0.5);
blackWonText.x = 1024;
blackWonText.y = 1366;
game.addChild(blackWonText);
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
// White won
var whiteWonText = new Text2('White Won!', {
size: 120,
fill: 0xFFFFFF
});
whiteWonText.anchor.set(0.5, 0.5);
whiteWonText.x = 1024;
whiteWonText.y = 1366;
game.addChild(whiteWonText);
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
}
}, 1000);
}
} else {
// Check warning - player is in check but has moves
LK.effects.flashScreen(0xff0000, 500);
LK.getSound('check').play();
}
} else if (validMoves.length === 0) {
// Stalemate - no check but no valid moves
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
}
function showPromotionDialog(color) {
if (promotionDialog) {
promotionDialog.destroy();
}
promotionDialog = new PromotionDialog(color, function (selectedPiece) {
handlePromotion(selectedPiece);
});
game.addChild(promotionDialog);
}
function handlePromotion(newPieceType) {
if (!pendingPromotion) return;
var oldPiece = pendingPromotion.piece;
var row = pendingPromotion.row;
var col = pendingPromotion.col;
// Remove old pawn
oldPiece.destroy();
for (var i = pieces.length - 1; i >= 0; i--) {
if (pieces[i] === oldPiece) {
pieces.splice(i, 1);
break;
}
}
board[row][col] = null;
// Create new piece
var newPiece = new ChessPiece(newPieceType, oldPiece.color, row, col);
newPiece.setPosition(row, col);
newPiece.hasMoved = true;
board[row][col] = newPiece;
pieces.push(newPiece);
game.addChild(newPiece);
// Clean up
promotionDialog.destroy();
promotionDialog = null;
pendingPromotion = null;
// Switch players
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
// Update timer display for online mode
if (gameMode === 'online') {
updateTimerDisplay();
lastTimerUpdate = Date.now();
}
// Check for game over conditions
checkGameOver();
}
function handleAIPromotion(oldPiece, row, col, newPieceType) {
// Remove old pawn
oldPiece.destroy();
for (var i = pieces.length - 1; i >= 0; i--) {
if (pieces[i] === oldPiece) {
pieces.splice(i, 1);
break;
}
}
board[row][col] = null;
// Create new piece
var newPiece = new ChessPiece(newPieceType, oldPiece.color, row, col);
newPiece.setPosition(row, col);
newPiece.hasMoved = true;
board[row][col] = newPiece;
pieces.push(newPiece);
game.addChild(newPiece);
// Switch players
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
if (gameMode === 'computer') {
isPlayerTurn = currentPlayer !== aiColor;
}
// Check for game over conditions
checkGameOver();
// Continue game flow after promotion - ensure AI can continue playing
if (gameMode === 'computer' && currentPlayer === aiColor && !gameOver) {
// If it's AI's turn after promotion, schedule another AI move
LK.setTimeout(function () {
makeAIMove();
}, 800);
}
}
function setPlayerPerspective(perspective) {
playerPerspective = perspective;
// Clear existing board and pieces
for (var i = pieces.length - 1; i >= 0; i--) {
pieces[i].destroy();
}
pieces = [];
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
board[row][col] = null;
squares[row][col].destroy();
}
}
// Clear coordinate labels
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Text2 && child !== turnText) {
child.destroy();
}
}
// Reinitialize board
for (var row = 0; row < 8; row++) {
board[row] = [];
squares[row] = [];
for (var col = 0; col < 8; col++) {
board[row][col] = null;
var square = new ChessSquare(row, col);
if (playerPerspective === 'black') {
square.x = boardOffsetX + (7 - col) * 200 + 100;
square.y = boardOffsetY + (7 - row) * 200 + 100;
} else {
square.x = boardOffsetX + col * 200 + 100;
square.y = boardOffsetY + row * 200 + 100;
}
squares[row][col] = square;
game.addChild(square);
}
}
// Reinitialize pieces and coordinates
initializePieces();
addCoordinateLabels();
}
function addCoordinateLabels() {
// Add numbers 1-8 on the left side (always from white's perspective)
for (var i = 0; i < 8; i++) {
var rankNumber, rankY;
if (playerPerspective === 'black') {
rankNumber = i + 1;
rankY = boardOffsetY + i * 200 + 100;
} else {
rankNumber = 8 - i;
rankY = boardOffsetY + i * 200 + 100;
}
var rankText = new Text2(rankNumber.toString(), {
size: 40,
fill: 0xFFFFFF
});
rankText.anchor.set(0.5, 0.5);
rankText.x = boardOffsetX - 60;
rankText.y = rankY;
game.addChild(rankText);
}
// Add letters a-h (always from white's perspective)
var files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
for (var i = 0; i < 8; i++) {
var fileLabel, fileX;
if (playerPerspective === 'black') {
fileLabel = files[7 - i];
fileX = boardOffsetX + i * 200 + 100;
} else {
fileLabel = files[i];
fileX = boardOffsetX + i * 200 + 100;
}
var fileText = new Text2(fileLabel, {
size: 40,
fill: 0xFFFFFF
});
fileText.anchor.set(0.5, 0.5);
fileText.x = fileX;
fileText.y = boardOffsetY + 8 * 200 + 60;
game.addChild(fileText);
}
}
function createTurnIndicator() {
// Add turn indicator
turnText = new Text2('White to move', {
size: 80,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.x = 1024;
turnText.y = 200;
game.addChild(turnText);
}
function createTimerDisplay() {
if (gameMode !== 'online') return;
// White timer
whiteTimerText = new Text2(formatTime(whiteTimeRemaining), {
size: 60,
fill: 0xFFFFFF
});
whiteTimerText.anchor.set(0.5, 0.5);
whiteTimerText.x = boardOffsetX + 1600 - 80;
whiteTimerText.y = boardOffsetY + 1600 + 120;
game.addChild(whiteTimerText);
// Black timer
blackTimerText = new Text2(formatTime(blackTimeRemaining), {
size: 60,
fill: 0xFFFFFF
});
blackTimerText.anchor.set(0.5, 0.5);
blackTimerText.x = boardOffsetX + 1600 - 80;
blackTimerText.y = boardOffsetY - 60;
game.addChild(blackTimerText);
}
function formatTime(seconds) {
var minutes = Math.floor(seconds / 60);
var remainingSeconds = seconds % 60;
return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds;
}
function startGameTimer() {
if (gameMode !== 'online') return;
lastTimerUpdate = Date.now();
gameTimer = LK.setInterval(function () {
var currentTime = Date.now();
var deltaTime = Math.floor((currentTime - lastTimerUpdate) / 1000);
if (deltaTime >= 1) {
if (currentPlayer === 'white') {
whiteTimeRemaining -= deltaTime;
if (whiteTimeRemaining <= 0) {
whiteTimeRemaining = 0;
handleTimeOut('white');
return;
}
} else {
blackTimeRemaining -= deltaTime;
if (blackTimeRemaining <= 0) {
blackTimeRemaining = 0;
handleTimeOut('black');
return;
}
}
updateTimerDisplay();
lastTimerUpdate = currentTime;
}
}, 100);
}
function updateTimerDisplay() {
if (gameMode !== 'online') return;
if (whiteTimerText) {
whiteTimerText.setText(formatTime(whiteTimeRemaining));
// Highlight current player's timer
if (currentPlayer === 'white') {
whiteTimerText.tint = 0x00ff00;
blackTimerText.tint = 0xffffff;
} else {
whiteTimerText.tint = 0xffffff;
blackTimerText.tint = 0x00ff00;
}
}
if (blackTimerText) {
blackTimerText.setText(formatTime(blackTimeRemaining));
}
}
function handleTimeOut(timedOutPlayer) {
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
gameOver = true;
LK.getSound('gameOver').play();
LK.setTimeout(function () {
if (timedOutPlayer === 'white') {
LK.showGameOver();
} else {
LK.showYouWin();
}
}, 1000);
}
function stopGameTimer() {
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
}
// Game starts with main menu - no immediate initialization
game.update = function () {
// Only update turn indicator if not in menu and turnText exists
if (gameMode !== 'menu' && turnText && !gameOver) {
var checkText = '';
var currentPlayerInCheck = isKingInCheck(currentPlayer);
var validMoves = getValidMovesForPlayer(currentPlayer);
if (currentPlayerInCheck) {
if (validMoves.length === 0) {
checkText = ' - CHECKMATE!';
} else {
checkText = ' - CHECK!';
}
}
var turnDisplay = '';
if (gameMode === 'computer') {
if (currentPlayer === playerPerspective) {
turnDisplay = 'Your turn';
} else {
turnDisplay = 'AI thinking...';
}
} else if (gameMode === 'online') {
turnDisplay = currentPlayer === 'white' ? 'White to move' : 'Black to move';
// Add time pressure warning
if (currentPlayer === 'white' && whiteTimeRemaining <= 60) {
turnDisplay += ' (LOW TIME!)';
} else if (currentPlayer === 'black' && blackTimeRemaining <= 60) {
turnDisplay += ' (LOW TIME!)';
}
} else {
turnDisplay = currentPlayer === 'white' ? 'White to move' : 'Black to move';
}
turnText.setText(turnDisplay + checkText);
}
};