/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BotSelectionScreen = Container.expand(function () {
var self = Container.call(this);
// Bot descriptions and icons
var botData = {
Martin: {
description: 'Martin makes many bad moves.',
icon: 'martinIcon'
},
Lina: {
description: 'Lina has an offensive strategy.',
icon: 'linaIcon'
},
Carl: {
description: 'Carl tries to stall the game.',
icon: 'carlIcon'
},
Wizard: {
description: 'Wizard uses magic for great moves.',
icon: 'wizardIcon'
},
King: {
description: 'King plays optimally but lacks a Queen.',
icon: 'kingIcon'
}
};
var bots = ['Martin', 'Lina', 'Carl', 'Wizard', 'King'];
// Assume these assets ('martinIcon', 'linaIcon', etc.) are defined or will be auto-created by the engine.
// Example sizes used here are 80x80.
var startY = -350; // Adjusted start Y for more spacing overall
var spacingY = 180; // Increased vertical spacing between buttons
// Create title text
var titleText = new Text2('Select Your Opponent', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = startY - 100; // Position title above the first button container
self.addChild(titleText);
// Store button containers and backgrounds for interaction setup
var buttonContainers = [];
var buttonBackgrounds = [];
// Create bot buttons with icons and descriptions using Containers
for (var i = 0; i < bots.length; i++) {
var botName = bots[i];
var currentBotData = botData[botName];
// Create a container for each button entry
var buttonContainer = new Container();
buttonContainer.x = 0; // Center container horizontally relative to parent (BotSelectionScreen)
buttonContainer.y = startY + i * spacingY; // Position container vertically
self.addChild(buttonContainer);
buttonContainers.push(buttonContainer); // Store the container
// Get icon asset
var icon = LK.getAsset(currentBotData.icon, {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80
});
icon.x = -200; // Position icon significantly to the left within the container
icon.y = 0; // Center icon vertically within the container
// Get bot name text asset
var botButtonText = new Text2(botName.toUpperCase(), {
size: 60,
fill: 0xFFFFFF
});
botButtonText.anchor.set(0, 0.5); // Anchor text to its left-middle
botButtonText.x = icon.x + icon.width / 2 + 20; // Position text right of the icon center + padding
botButtonText.y = -15; // Position text slightly above the vertical center
// Get description text asset
var descriptionText = new Text2(currentBotData.description, {
size: 30,
fill: 0xCCCCCC
});
descriptionText.anchor.set(0, 0.5); // Anchor text to its left-middle
descriptionText.x = botButtonText.x; // Align description start with bot name start
descriptionText.y = 15; // Position description below the vertical center
// Calculate dynamic width/height needed for the background based on content
var textBlockWidth = Math.max(botButtonText.width, descriptionText.width);
var contentWidth = icon.width + 20 + textBlockWidth; // Icon width + padding + max text width
var contentHeight = Math.max(icon.height, botButtonText.height + descriptionText.height + 10); // Max of icon height or combined text height + spacing
var bgWidth = contentWidth + 80; // Add horizontal padding to content width
var bgHeight = contentHeight + 40; // Add vertical padding to content height
// Create background asset
var bg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: bgWidth,
height: bgHeight,
tint: 0x3a3a3a
});
bg.alpha = 0.7;
// Center the background horizontally around the content block
bg.x = icon.x - icon.width / 2 + contentWidth / 2;
bg.y = 0; // Center background vertically in the container
buttonContainer.addChild(bg); // Add background first so text/icon are on top
buttonBackgrounds.push(bg); // Store background reference for interaction effects
// Add icon and text elements on top of the background
buttonContainer.addChild(icon);
buttonContainer.addChild(botButtonText);
buttonContainer.addChild(descriptionText);
// Make the entire container interactive
buttonContainer.interactive = true;
buttonContainer.botIndex = i; // Store the bot index on the container for the click handler
}
// Create Back button text
var backButton = new Text2('< BACK', {
size: 60,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
// Position back button below the last bot container, accounting for increased spacing
backButton.y = startY + bots.length * spacingY + 60;
self.addChild(backButton);
// Create Back button background
var backBg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: backButton.width + 80,
height: backButton.height + 40,
tint: 0x3a3a3a
});
backBg.alpha = 0.7;
backBg.x = backButton.x; // Align background center with text center
backBg.y = backButton.y; // Align background center with text center
self.addChild(backBg); // Add background first
self.addChild(backButton); // Add text on top of background
// --- Interaction Setup ---
// Bot button interactions (using stored containers and backgrounds)
for (var i = 0; i < buttonContainers.length; i++) {
(function (index) {
var btnContainer = buttonContainers[index];
var bg = buttonBackgrounds[index]; // Get the corresponding background
// Attach down event to the container
btnContainer.down = function (x, y, obj) {
// Flash the background alpha on click
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
// Return alpha to normal after flash
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Handle button action: select bot and start game
game.selectedBot = bots[index]; // Use stored index to get bot name
game.startGame();
};
})(i);
}
// Back button interaction
(function () {
var btn = backButton; // Reference to the Text2 object
var bg = backBg; // Reference to the background shape
btn.interactive = true; // Make the Text2 object interactive
// Attach down event to the text object
btn.down = function (x, y, obj) {
// Flash the background alpha on click
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
// Return alpha to normal after flash
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Handle back button action: show the previous screen
game.showGameModeScreen();
};
})();
// Update function (can be used for animations later if needed)
self.update = function () {
// Optional: Add animations like pulsing if needed
};
return self;
});
var ChessPiece = Container.expand(function (type, color) {
var self = Container.call(this);
self.type = type;
self.color = color;
self.hasMoved = false;
var pieceId = color + type.charAt(0).toUpperCase() + type.slice(1);
var pieceGraphics = self.attachAsset(pieceId, {
anchorX: 0.5,
anchorY: 0.5
});
var label;
switch (type) {
case 'pawn':
label = '';
break;
case 'rook':
label = 'R';
break;
case 'knight':
label = 'N';
break;
case 'bishop':
label = 'B';
break;
case 'queen':
label = 'Q';
break;
case 'king':
label = 'K';
break;
}
if (label) {
var text = new Text2(label, {
size: 40,
fill: color === 'white' ? "#000000" : "#ffffff"
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
}
return self;
});
var ChessSquare = Container.expand(function (row, col, color) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.color = color;
self.piece = null;
var squareGraphics = self.attachAsset(color === 'white' ? 'whiteSquare' : 'blackSquare', {
anchorX: 0,
anchorY: 0
});
self.highlight = function (type) {
if (type === 'selected') {
squareGraphics.tint = 0x829769;
} else if (type === 'validMove') {
squareGraphics.tint = 0xcdd26a;
} else {
// Reset to original color
squareGraphics.tint = color === 'white' ? 0xf0d9b5 : 0xb58863;
}
};
self.setPiece = function (piece) {
if (self.piece) {
self.removeChild(self.piece);
}
self.piece = piece;
if (piece) {
self.addChild(piece);
piece.x = squareGraphics.width / 2;
piece.y = squareGraphics.height / 2;
}
};
self.down = function (x, y, obj) {
game.selectSquare(self);
};
return self;
});
var CrazyAbility = Container.expand(function () {
var self = Container.call(this);
// Available abilities
self.abilities = [{
name: 'Freeze',
description: 'Freezes a white piece for 2 turns'
}, {
name: 'Instant Promotion',
description: 'Promotes a black pawn to a powerful piece'
}, {
name: 'Stampede',
description: 'Removes 3 random pieces from the board'
}, {
name: 'Summon',
description: 'Summons a black rook on an empty square'
}, {
name: 'Teleport',
description: 'Teleports one of your pieces to danger!'
}, {
name: 'Goatify',
description: 'Turns a piece into a goat that moves like a king for 2 turns'
}, {
name: 'Trap',
description: 'Places a bear trap that captures pieces that land on it'
}];
// Stores pieces that are frozen
self.frozenPieces = [];
// Use a random ability
self.useRandomAbility = function () {
var randomIndex = Math.floor(Math.random() * self.abilities.length);
var ability = self.abilities[randomIndex];
// Show ability notification
self.showAbilityNotification(ability.name);
// Execute ability
switch (ability.name) {
case 'Freeze':
self.freezeRandomPiece();
break;
case 'Instant Promotion':
self.instantPromotion();
break;
case 'Stampede':
self.stampede();
break;
case 'Summon':
self.summonRook();
break;
case 'Teleport':
self.teleport();
break;
case 'Goatify':
self.goatifyPiece();
break;
case 'Trap':
self.placeTrap();
break;
}
};
// Show notification of ability used
self.showAbilityNotification = function (abilityName) {
var notification = new Text2('AI USED: ' + abilityName + '!', {
size: 50,
fill: 0xFF0000
});
notification.anchor.set(0.5, 0.5);
notification.x = 2048 / 2;
notification.y = 2732 / 2 - 300;
game.addChild(notification);
// Flash effect
LK.effects.flashObject(notification, 0xFF0000, 500);
// Remove after delay
LK.setTimeout(function () {
game.removeChild(notification);
}, 2000);
};
// Freeze a random white piece for 2 turns
self.freezeRandomPiece = function () {
var whitePieces = [];
// Find all white pieces
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'white') {
whitePieces.push({
square: square,
row: row,
col: col
});
}
}
}
if (whitePieces.length > 0) {
// Select random white piece
var randomIndex = Math.floor(Math.random() * whitePieces.length);
var targetPiece = whitePieces[randomIndex];
// Add to frozen list with 2 turns duration
self.frozenPieces.push({
row: targetPiece.row,
col: targetPiece.col,
turnsLeft: 2
});
// Visual effect - tint blue
targetPiece.square.piece.tint = 0x00AAFF;
}
};
// Turn a random black pawn into a queen, bishop or rook
self.instantPromotion = function () {
var blackPawns = [];
// Find all black pawns
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'black' && square.piece.type === 'pawn') {
blackPawns.push({
square: square
});
}
}
}
if (blackPawns.length > 0) {
// Select random black pawn
var randomIndex = Math.floor(Math.random() * blackPawns.length);
var targetPawn = blackPawns[randomIndex];
// Choose random promotion type
var promotionTypes = ['queen', 'bishop', 'rook'];
var randomType = promotionTypes[Math.floor(Math.random() * promotionTypes.length)];
// Promote the pawn
targetPawn.square.setPiece(new ChessPiece(randomType, 'black'));
LK.getSound('promotion').play();
}
};
// Remove 3 random pieces from the board
self.stampede = function () {
var allPieces = [];
// Find all pieces, excluding all kings
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.type !== 'king') {
allPieces.push({
square: square
});
}
}
}
// Remove up to 3 random pieces
var removeCount = Math.min(3, allPieces.length);
for (var i = 0; i < removeCount; i++) {
if (allPieces.length > 0) {
var randomIndex = Math.floor(Math.random() * allPieces.length);
var targetPiece = allPieces[randomIndex];
// Remove piece
targetPiece.square.setPiece(null);
// Remove from array
allPieces.splice(randomIndex, 1);
}
}
LK.getSound('capture').play();
};
// Summon a black rook on a random empty square
self.summonRook = function () {
var emptySquares = [];
// Find all empty squares
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (!square.piece) {
emptySquares.push({
square: square
});
}
}
}
if (emptySquares.length > 0) {
// Select random empty square
var randomIndex = Math.floor(Math.random() * emptySquares.length);
var targetSquare = emptySquares[randomIndex];
// Add new rook
targetSquare.square.setPiece(new ChessPiece('rook', 'black'));
}
};
// Decrement frozen piece counters
self.updateFrozenPieces = function () {
for (var i = self.frozenPieces.length - 1; i >= 0; i--) {
self.frozenPieces[i].turnsLeft--;
// If no turns left, unfreeze the piece
if (self.frozenPieces[i].turnsLeft <= 0) {
var row = self.frozenPieces[i].row;
var col = self.frozenPieces[i].col;
// Only unfreeze if piece still exists at that position
if (board[row][col].piece && board[row][col].piece.color === 'white') {
board[row][col].piece.tint = 0xFFFFFF;
}
self.frozenPieces.splice(i, 1);
}
}
};
// Teleport a white piece to a dangerous position
self.teleport = function () {
var whitePieces = [];
var dangerSquares = [];
// Find all white pieces (except king)
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'white' && square.piece.type !== 'king') {
whitePieces.push({
square: square,
row: row,
col: col
});
}
}
}
// Find all squares under attack by black pieces
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
// Check if this square is attacked by any black piece
if (isSquareAttacked(row, col, 'black') && !board[row][col].piece) {
dangerSquares.push({
row: row,
col: col
});
}
}
}
// If we have white pieces and danger squares
if (whitePieces.length > 0 && dangerSquares.length > 0) {
// Select random white piece
var randomPieceIndex = Math.floor(Math.random() * whitePieces.length);
var targetPiece = whitePieces[randomPieceIndex];
// Select random danger square
var randomSquareIndex = Math.floor(Math.random() * dangerSquares.length);
var dangerSquare = dangerSquares[randomSquareIndex];
// Teleport the piece
var piece = targetPiece.square.piece;
targetPiece.square.setPiece(null);
board[dangerSquare.row][dangerSquare.col].setPiece(piece);
// Visual effect - flash the teleported piece
LK.effects.flashObject(board[dangerSquare.row][dangerSquare.col].piece, 0xFF00FF, 500);
}
};
// Stores goatified pieces data
self.goatifiedPieces = [];
// Turn a white piece into a goat (moves like king)
self.goatifyPiece = function () {
var whitePieces = [];
// Find all white pieces except the king (king is immune)
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'white' && square.piece.type !== 'king') {
whitePieces.push({
square: square,
row: row,
col: col,
type: square.piece.type
});
}
}
}
if (whitePieces.length > 0) {
// Select random white piece
var randomIndex = Math.floor(Math.random() * whitePieces.length);
var targetPiece = whitePieces[randomIndex];
// Add to goatified list with 2 turns duration
self.goatifiedPieces.push({
row: targetPiece.row,
col: targetPiece.col,
originalType: targetPiece.type,
turnsLeft: 2
});
// Visual effect - tint piece to goat color
targetPiece.square.piece.tint = 0x88BB33;
// Show a goat indicator
var goatText = new Text2("π", {
size: 30,
fill: 0xFFFFFF
});
goatText.anchor.set(0.5, 0.5);
goatText.x = targetPiece.square.x + SQUARE_SIZE / 2;
goatText.y = targetPiece.square.y + SQUARE_SIZE / 2 - 30;
game.addChild(goatText);
// Remove indicator after a short time
LK.setTimeout(function () {
game.removeChild(goatText);
}, 1500);
}
};
// Array to track trap positions
self.trapSquares = [];
// Place a bear trap on a random empty square
self.placeTrap = function () {
var emptySquares = [];
// Find all empty squares
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (!square.piece) {
emptySquares.push({
row: row,
col: col
});
}
}
}
if (emptySquares.length > 0) {
// Select random empty square
var randomIndex = Math.floor(Math.random() * emptySquares.length);
var trapSquare = emptySquares[randomIndex];
// Add trap to the list
self.trapSquares.push({
row: trapSquare.row,
col: trapSquare.col
});
// Visual indication of trap
var trapIndicator = new Text2("β οΈ", {
size: 40,
fill: 0xFF5500
});
trapIndicator.anchor.set(0.5, 0.5);
trapIndicator.x = board[trapSquare.row][trapSquare.col].x + SQUARE_SIZE / 2;
trapIndicator.y = board[trapSquare.row][trapSquare.col].y + SQUARE_SIZE / 2;
game.addChild(trapIndicator);
board[trapSquare.row][trapSquare.col].trapIndicator = trapIndicator;
}
};
// Update goatified pieces counters
self.updateGoatifiedPieces = function () {
for (var i = self.goatifiedPieces.length - 1; i >= 0; i--) {
self.goatifiedPieces[i].turnsLeft--;
// If no turns left, restore the piece to its original type
if (self.goatifiedPieces[i].turnsLeft <= 0) {
var row = self.goatifiedPieces[i].row;
var col = self.goatifiedPieces[i].col;
var originalType = self.goatifiedPieces[i].originalType;
// Only restore if piece still exists at that position and is white
if (board[row][col].piece && board[row][col].piece.color === 'white') {
// Reset tint
board[row][col].piece.tint = 0xFFFFFF;
}
self.goatifiedPieces.splice(i, 1);
}
}
};
// Check if a piece is goatified
self.isPieceGoatified = function (row, col) {
for (var i = 0; i < self.goatifiedPieces.length; i++) {
if (self.goatifiedPieces[i].row === row && self.goatifiedPieces[i].col === col) {
return true;
}
}
return false;
};
// Check if a square has a trap
self.hasTrap = function (row, col) {
for (var i = 0; i < self.trapSquares.length; i++) {
if (self.trapSquares[i].row === row && self.trapSquares[i].col === col) {
return true;
}
}
return false;
};
// Trigger trap on a square and remove it
self.triggerTrap = function (row, col) {
for (var i = 0; i < self.trapSquares.length; i++) {
if (self.trapSquares[i].row === row && self.trapSquares[i].col === col) {
// Remove the trap indicator
if (board[row][col].trapIndicator) {
game.removeChild(board[row][col].trapIndicator);
board[row][col].trapIndicator = null;
}
// Remove the trap from our list
self.trapSquares.splice(i, 1);
return true;
}
}
return false;
};
// Check if a piece is frozen
self.isPieceFrozen = function (row, col) {
for (var i = 0; i < self.frozenPieces.length; i++) {
if (self.frozenPieces[i].row === row && self.frozenPieces[i].col === col) {
return true;
}
}
return false;
};
return self;
});
var DailyChallengeScreen = Container.expand(function () {
var self = Container.call(this);
// Special rules collection - now the only focus of daily challenges
var specialRules = [{
name: 'Fog of War',
description: 'You can only see squares your pieces can move to.',
id: 'fogOfWar'
}, {
name: 'Backwards Pawns',
description: 'Pawns can move and capture backwards.',
id: 'backwardsPawns'
}, {
name: 'Capture the King',
description: 'No check/checkmate. Capture the enemy king to win!',
id: 'captureKing'
}, {
name: 'SHREDR Bishops',
description: 'Bishops can jump over pieces and move one square horizontally.',
id: 'shredrBishops'
}, {
name: 'Small Chess',
description: 'Play on a 4x8 board without knights or bishops.',
id: 'smallChess'
}, {
name: 'Transformo-fest',
description: 'Pieces (except kings) randomly transform after moving!',
id: 'transformoFest'
}, {
name: 'Dragon Boss',
description: 'Capture the Dragon (Queen+Knight moves) using 3 Knights & 4 Bishops.',
id: 'dragonBoss'
}, {
name: 'GOAT-ed Challenge',
description: 'Your pieces (except king) are goats! Survive 25 turns.',
id: 'goatedChallenge'
}, {
name: 'Icy Day',
description: 'Pieces may freeze after moving! Some pawns are immune Yetis.',
id: 'icyDay'
}];
// Select a random special rule for the "day"
var selectedSpecialRule = specialRules[Math.floor(Math.random() * specialRules.length)];
// --- UI Elements ---
// Title Text
var titleText = new Text2('Daily Challenge: Special Game', {
size: 70,
fill: 0xFFFFFF,
wordWrap: true,
// Enable word wrap
wordWrapWidth: 1800 // Set max width before wrapping
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -350; // Position higher up
self.addChild(titleText);
// Compile description for the selected special rule
var finalDescription = 'Special Rule: ' + selectedSpecialRule.name + '\n' + selectedSpecialRule.description;
// Description Text
var descriptionText = new Text2(finalDescription, {
size: 45,
fill: 0xCCCCCC,
align: 'center',
// Center align text
wordWrap: true,
// Enable word wrap
wordWrapWidth: 1600 // Set max width before wrapping
});
descriptionText.anchor.set(0.5, 0.5);
descriptionText.y = -150; // Position below title
self.addChild(descriptionText);
// Start Challenge Button
var startButton = new Text2('START CHALLENGE', {
size: 70,
fill: 0xFFFFFF
});
startButton.anchor.set(0.5, 0.5);
startButton.y = 100;
self.addChild(startButton);
// Back Button
var backButton = new Text2('< BACK', {
size: 60,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
backButton.y = 300; // Position further down
self.addChild(backButton);
// Add backgrounds for buttons
var buttons = [startButton, backButton];
var buttonBackgrounds = [];
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var bg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: btn.width + 80,
height: btn.height + 40,
tint: 0x3a3a3a
});
bg.alpha = 0.7;
bg.x = btn.x;
bg.y = btn.y;
buttonBackgrounds.push(bg);
self.addChild(bg);
self.addChild(btn); // Re-add button to put it on top
}
// --- Interactions ---
// Start Button Interaction
(function (index) {
var btn = buttons[index];
var bg = buttonBackgrounds[index];
btn.interactive = true;
btn.down = function (x, y, obj) {
// Flash effect
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Set the game mode and the specific rule
game.gameMode = 'specialGame';
game.specialRule = selectedSpecialRule.id; // Store the ID of the rule
// Remove this screen
if (game.dailyChallengeScreen) {
game.removeChild(game.dailyChallengeScreen);
game.dailyChallengeScreen = null;
}
game.showBotSelectionScreen(); // Go to bot selection for special game
};
})(0); // Index 0 is the start button
// Back Button Interaction
(function (index) {
var btn = buttons[index];
var bg = buttonBackgrounds[index];
btn.interactive = true;
btn.down = function (x, y, obj) {
// Flash effect
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Go back to the game mode selection screen
game.showGameModeScreen();
};
})(1); // Index 1 is the back button
self.update = function () {
// Optional: Add animations if needed
};
return self;
});
var GameModeScreen = Container.expand(function () {
var self = Container.call(this);
// Create title text
var titleText = new Text2('Select Game Mode', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -250;
self.addChild(titleText);
// Create mode buttons
var classicButton = new Text2('CLASSIC', {
size: 80,
fill: 0xFFFFFF
});
classicButton.anchor.set(0.5, 0.5);
classicButton.y = -150;
self.addChild(classicButton);
var crazyButton = new Text2('CRAZY MODE', {
size: 80,
fill: 0xFFFFFF
});
crazyButton.anchor.set(0.5, 0.5);
crazyButton.y = 0;
self.addChild(crazyButton);
var dailyChallengeButton = new Text2('DAILY CHALLENGE', {
size: 80,
fill: 0xFFFFFF
});
dailyChallengeButton.anchor.set(0.5, 0.5);
dailyChallengeButton.y = 150;
self.addChild(dailyChallengeButton);
var backButton = new Text2('< BACK', {
size: 60,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
backButton.y = 350;
self.addChild(backButton);
// Add highlighting for buttons
var buttons = [classicButton, crazyButton, dailyChallengeButton, backButton];
var buttonBackgrounds = [];
// Create button backgrounds
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var bg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: btn.width + 80,
height: btn.height + 40,
tint: 0x3a3a3a
});
bg.alpha = 0.7;
bg.x = btn.x;
bg.y = btn.y;
buttonBackgrounds.push(bg);
self.addChild(bg);
self.addChild(btn); // Re-add button to put it on top
}
// Add button hover effects
for (var i = 0; i < buttons.length; i++) {
(function (index) {
var btn = buttons[index];
var bg = buttonBackgrounds[index];
btn.interactive = true;
btn.down = function (x, y, obj) {
// Flash button when clicked
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Handle button action
if (index === 0) {
// Classic mode
game.gameMode = 'classic';
game.showBotSelectionScreen(); // Go to bot selection
} else if (index === 1) {
// Crazy mode
game.gameMode = 'crazy';
game.showBotSelectionScreen(); // Go to bot selection
} else if (index === 2) {
// Daily Challenge mode
game.gameMode = 'dailyChallenge';
game.showDailyChallengeScreen(); // Show the dedicated screen
} else if (index === 3) {
// Back button
game.showTitleScreen();
}
};
})(i);
}
// Pulse animation for the first button
var pulseScale = 1;
var increasing = true;
self.update = function () {
// Pulse effect for the Classic button
if (increasing) {
pulseScale += 0.002;
if (pulseScale >= 1.05) {
increasing = false;
}
} else {
pulseScale -= 0.002;
if (pulseScale <= 0.95) {
increasing = true;
}
}
classicButton.scale.set(pulseScale);
buttonBackgrounds[0].scale.set(pulseScale);
};
return self;
});
var TitleScreen = Container.expand(function () {
var self = Container.call(this);
// Create title text
var titleText = new Text2('Crazy Chess', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
self.addChild(titleText);
// Create start button
var startButton = new Text2('TAP TO START', {
size: 90,
fill: 0xFFFFFF
});
startButton.anchor.set(0.5, 0.5);
startButton.y = 250;
self.addChild(startButton);
// Add pulsing animation to the start button
var pulseScale = 1;
var increasing = true;
self.update = function () {
// Pulse effect for start button
if (increasing) {
pulseScale += 0.003;
if (pulseScale >= 1.1) {
increasing = false;
}
} else {
pulseScale -= 0.003;
if (pulseScale <= 0.9) {
increasing = true;
}
}
startButton.scale.set(pulseScale);
};
// Handle tap anywhere to start
self.down = function () {
game.showGameModeScreen();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game constants - Default to 8x8
var BOARD_SIZE = 8; // Default board size
var BOARD_SIZE_X = 8;
var BOARD_SIZE_Y = 8;
var SQUARE_SIZE = 100;
var BOARD_OFFSET_X = (2048 - BOARD_SIZE_X * SQUARE_SIZE) / 2;
var BOARD_OFFSET_Y = (2732 - BOARD_SIZE_Y * SQUARE_SIZE) / 2;
var BOARD_START_COL = 0; // For small chess adjustment
var BOARD_END_COL = 7; // For small chess adjustment
// Game state variables
var board = [];
var selectedSquare = null;
var validMoves = [];
var currentPlayer = 'white';
var gameState = 'title'; // 'title', 'modeSelect', 'botSelect', 'playing', 'check', 'checkmate', 'stalemate', 'dailyChallenge', 'specialGame'
var specialRule = null; // Stores the active special rule for daily challenges
var aiThinking = false;
var titleScreen = null;
var gameModeScreen = null;
var botSelectionScreen = null; // Add variable for the bot selection screen
var dailyChallengeScreen = null; // Add variable for the daily challenge screen
var selectedBot = ''; // Add variable to store the selected bot
// gameMode is now set on the game object, not as a separate variable
var crazyAbilities = new CrazyAbility();
var crazyAbilityChance = 0.4; // 40% chance of AI using ability after a move
// Special Mode Variables
var goatedTurnCounter = 0;
var ICY_DAY_FREEZE_CHANCE = 0.3; // 30% chance to freeze
var ICY_DAY_FREEZE_TURNS = 2; // Freeze duration
var icyDayFrozenPieces = []; // Stores { row, col, turnsLeft } for Icy Day
var yetiPawns = []; // Stores { row, col } of Yeti pawns
// Variables for draw conditions
var movesSinceCaptureOrPawn = 0; // For 50-move rule
var lastMove = null; // To track piece captures and pawn moves
// Initialize the chess board
function initializeBoard() {
board = [];
// Adjust board size and offsets for Small Chess
if (game.gameMode === 'specialGame' && game.specialRule === 'smallChess') {
BOARD_SIZE_X = 4;
BOARD_SIZE_Y = 8;
BOARD_START_COL = 2;
BOARD_END_COL = 5;
BOARD_OFFSET_X = (2048 - BOARD_SIZE_X * SQUARE_SIZE) / 2;
BOARD_OFFSET_Y = (2732 - BOARD_SIZE_Y * SQUARE_SIZE) / 2;
} else {
// Reset to default if not small chess
BOARD_SIZE_X = 8;
BOARD_SIZE_Y = 8;
BOARD_START_COL = 0;
BOARD_END_COL = 7;
BOARD_OFFSET_X = (2048 - BOARD_SIZE_X * SQUARE_SIZE) / 2;
BOARD_OFFSET_Y = (2732 - BOARD_SIZE_Y * SQUARE_SIZE) / 2;
}
// Create the chess board based on dimensions
for (var row = 0; row < BOARD_SIZE_Y; row++) {
board[row] = [];
for (var col = 0; col < BOARD_SIZE_X; col++) {
var boardCol = col + BOARD_START_COL; // Map visual column to board array column
var squareColor = (row + boardCol) % 2 === 0 ? 'white' : 'black';
var square = new ChessSquare(row, boardCol, squareColor); // Store the actual board column
square.x = BOARD_OFFSET_X + col * SQUARE_SIZE; // Visual position based on 0 to BOARD_SIZE_X-1
square.y = BOARD_OFFSET_Y + row * SQUARE_SIZE;
// Initialize board array slots, only add visible squares to game
board[row][boardCol] = square; // Use actual board column index
game.addChild(square);
}
// Initialize non-visible columns for internal logic if needed (e.g. full 8x8 array)
if (game.gameMode !== 'specialGame' || game.specialRule !== 'smallChess') {
// Standard 8x8 initialization already covered
} else {
// For small chess, fill the rest of the 8x8 array rows with null or placeholder if needed by logic
// This example assumes logic primarily uses BOARD_START_COL and BOARD_END_COL checks
for (var r = 0; r < BOARD_SIZE_Y; ++r) {
for (var c = 0; c < 8; ++c) {
if (c < BOARD_START_COL || c > BOARD_END_COL) {
if (!board[r]) {
board[r] = [];
}
board[r][c] = null; // Or a placeholder indicating off-board
}
}
}
}
}
// Place the pieces on the board
placePieces();
}
// Place all chess pieces in their starting positions
function placePieces() {
if (game.gameMode === 'specialGame' && game.specialRule === 'smallChess') {
// Small Chess Setup (4x8 board, cols 2-5)
// Place pawns
for (var col = BOARD_START_COL; col <= BOARD_END_COL; col++) {
board[1][col].setPiece(new ChessPiece('pawn', 'black'));
board[6][col].setPiece(new ChessPiece('pawn', 'white'));
}
// Place rooks
board[0][BOARD_START_COL].setPiece(new ChessPiece('rook', 'black')); // Col 2
board[0][BOARD_END_COL].setPiece(new ChessPiece('rook', 'black')); // Col 5
board[7][BOARD_START_COL].setPiece(new ChessPiece('rook', 'white')); // Col 2
board[7][BOARD_END_COL].setPiece(new ChessPiece('rook', 'white')); // Col 5
// Place queens
board[0][BOARD_START_COL + 1].setPiece(new ChessPiece('queen', 'black')); // Col 3
board[7][BOARD_START_COL + 1].setPiece(new ChessPiece('queen', 'white')); // Col 3
// Place kings
board[0][BOARD_START_COL + 2].setPiece(new ChessPiece('king', 'black')); // Col 4
board[7][BOARD_START_COL + 2].setPiece(new ChessPiece('king', 'white')); // Col 4
// No knights or bishops
} else if (game.gameMode === 'specialGame' && game.specialRule === 'dragonBoss') {
// Dragon Boss Setup
// White: King, 4 Bishops, 3 Knights
board[7][4].setPiece(new ChessPiece('king', 'white')); // e1
board[7][2].setPiece(new ChessPiece('bishop', 'white')); // c1
board[7][3].setPiece(new ChessPiece('bishop', 'white')); // d1
board[7][5].setPiece(new ChessPiece('bishop', 'white')); // f1
board[7][6].setPiece(new ChessPiece('bishop', 'white')); // g1
board[7][1].setPiece(new ChessPiece('knight', 'white')); // b1
board[7][7].setPiece(new ChessPiece('knight', 'white')); // h1
board[6][4].setPiece(new ChessPiece('knight', 'white')); // e2 - One knight forward
// Black: Dragon (use Queen asset for now, moves handled separately)
board[0][4].setPiece(new ChessPiece('queen', 'black')); // Dragon on e8
board[0][4].piece.isDragon = true; // Mark as Dragon
} else {
// Standard Setup
// Place pawns
for (var col = 0; col < 8; col++) {
//{8q} // Use 8 explicitly
board[1][col].setPiece(new ChessPiece('pawn', 'black'));
board[6][col].setPiece(new ChessPiece('pawn', 'white'));
}
// Place rooks
board[0][0].setPiece(new ChessPiece('rook', 'black'));
board[0][7].setPiece(new ChessPiece('rook', 'black'));
board[7][0].setPiece(new ChessPiece('rook', 'white'));
board[7][7].setPiece(new ChessPiece('rook', 'white'));
// Place knights
board[0][1].setPiece(new ChessPiece('knight', 'black'));
board[0][6].setPiece(new ChessPiece('knight', 'black'));
board[7][1].setPiece(new ChessPiece('knight', 'white'));
board[7][6].setPiece(new ChessPiece('knight', 'white'));
// Place bishops
board[0][2].setPiece(new ChessPiece('bishop', 'black'));
board[0][5].setPiece(new ChessPiece('bishop', 'black'));
board[7][2].setPiece(new ChessPiece('bishop', 'white'));
board[7][5].setPiece(new ChessPiece('bishop', 'white'));
// Place queens
board[0][3].setPiece(new ChessPiece('queen', 'black'));
board[7][3].setPiece(new ChessPiece('queen', 'white'));
// Place kings
board[0][4].setPiece(new ChessPiece('king', 'black'));
board[7][4].setPiece(new ChessPiece('king', 'white'));
}
// GOAT-ed Challenge: Make all white pieces (except king) goatified
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge') {
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
if (board[r][c] && board[r][c].piece && board[r][c].piece.color === 'white' && board[r][c].piece.type !== 'king') {
board[r][c].piece.isPermanentlyGoatified = true;
board[r][c].piece.tint = 0x88BB33; // Goat color tint
}
}
}
goatedTurnCounter = 0; // Reset turn counter
}
// Icy Day: Designate some white pawns as Yetis
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay') {
icyDayFrozenPieces = []; // Reset frozen list
yetiPawns = []; // Reset yeti list
// Make pawns on b, d, e, g Yetis
var yetiCols = [1, 3, 4, 6]; // b, d, e, g columns
for (var i = 0; i < yetiCols.length; i++) {
var col = yetiCols[i];
if (board[6][col] && board[6][col].piece && board[6][col].piece.type === 'pawn') {
board[6][col].piece.isYeti = true;
board[6][col].piece.tint = 0xADD8E6; // Light blue tint for Yetis
yetiPawns.push({
row: 6,
col: col
});
}
}
}
// Special rule for 'King' bot: remove the black queen
if (game.selectedBot === 'King' && game.gameMode !== 'specialGame') {
// Don't apply if it's a special game
if (board[0][3].piece && board[0][3].piece.type === 'queen' && board[0][3].piece.color === 'black') {
board[0][3].setPiece(null);
}
}
}
// Select a square on the chess board
game.selectSquare = function (square) {
// If AI is thinking, don't allow moves
if (aiThinking || gameState === 'checkmate' || gameState === 'stalemate') {
return;
}
// If the current player is not the human player, don't allow moves
if (currentPlayer !== 'white') {
return;
}
// Check if piece is frozen (Only in Crazy Mode)
if (game.gameMode === 'crazy' && square.piece && square.piece.color === 'white' && crazyAbilities.isPieceFrozen(square.row, square.col)) {
// Show notification that piece is frozen
var notification = new Text2('This piece is frozen!', {
size: 40,
fill: 0x00AAFF
});
notification.anchor.set(0.5, 0.5);
notification.x = 2048 / 2;
notification.y = 2732 / 2 - 200;
game.addChild(notification);
// Remove notification after delay
LK.setTimeout(function () {
game.removeChild(notification);
}, 1500);
return;
}
// Check if piece is frozen (Icy Day Mode)
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay' && square.piece && square.piece.isFrozenIcy) {
// Show notification that piece is frozen
var notificationIcy = new Text2('This piece is frozen!', {
size: 40,
//{90} // Reuse ID if appropriate
fill: 0x00AAFF
}); //{91} // Reuse ID if appropriate
notificationIcy.anchor.set(0.5, 0.5); //{92} // Reuse ID if appropriate
notificationIcy.x = 2048 / 2; //{93} // Reuse ID if appropriate
notificationIcy.y = 2732 / 2 - 200;
game.addChild(notificationIcy); //{94} // Reuse ID if appropriate
// Remove notification after delay
LK.setTimeout(function () {
//{95} // Reuse ID if appropriate
game.removeChild(notificationIcy); //{96} // Reuse ID if appropriate
}, 1500); //{97} // Reuse ID if appropriate
return; // Don't allow selection
}
// Clear previous selections
clearHighlights();
// If a square is already selected and the new square is a valid move
if (selectedSquare && isValidMove(square)) {
// Move the piece
var from = selectedSquare;
var to = square;
movePiece(from, to);
// Reset selection state
selectedSquare = null;
validMoves = [];
clearHighlights(); // Clear highlights after move
// Check if the move ended the game via specific rules (like king capture)
// gameState might have been updated in movePiece
if (gameState === 'checkmate' || gameState === 'stalemate') {
// endGame was already called in movePiece
return;
}
// Switch to the next player
switchPlayer(); //{9C} // currentPlayer is now the opponent ('black')
// Now check the state for the player whose turn it just became
checkGameState(); //{9B} // Checks state for 'black'
// If the game ended due to checkmate/stalemate after the switch
if (gameState === 'checkmate' || gameState === 'stalemate') {
// Game end message already shown by checkGameState -> endGame
return;
}
// If it's AI's turn (now black) and game is still going
if (currentPlayer === 'black') {
aiThinking = true;
LK.setTimeout(makeAIMove, 1000);
}
}
// If clicking on a piece of the current player
else if (square.piece && square.piece.color === currentPlayer) {
// Select the square
selectedSquare = square;
square.highlight('selected');
// Calculate valid moves
validMoves = calculateValidMoves(square);
// Highlight valid moves
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
board[move.row][move.col].highlight('validMove');
}
}
};
// Check if a move to the target square is valid
function isValidMove(targetSquare) {
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (move.row === targetSquare.row && move.col === targetSquare.col) {
return true;
}
}
return false;
}
// Move a piece from one square to another
function movePiece(fromSquare, toSquare) {
var piece = fromSquare.piece;
// Check for pawn move or capture (for 50-move rule)
var isPawnMove = piece.type === 'pawn';
var isCapture = toSquare.piece !== null;
// Check for trap only in crazy mode
var trapTriggered = false;
if (game.gameMode === 'crazy' && crazyAbilities.hasTrap(toSquare.row, toSquare.col)) {
// Trigger trap effect
trapTriggered = crazyAbilities.triggerTrap(toSquare.row, toSquare.col);
// If trap is triggered and the piece is white (not king - king is immune)
if (trapTriggered && piece.color === 'white' && piece.type !== 'king') {
// Show trap activation message
var trapMessage = new Text2("TRAP TRIGGERED!", {
size: 50,
fill: 0xFF0000
});
trapMessage.anchor.set(0.5, 0.5);
trapMessage.x = 2048 / 2;
trapMessage.y = 2732 / 2 - 250;
game.addChild(trapMessage);
// Remove message after delay
LK.setTimeout(function () {
game.removeChild(trapMessage);
}, 1500);
// Piece is captured by the trap
fromSquare.setPiece(null);
LK.getSound('capture').play();
// Update move counter for 50-move rule
movesSinceCaptureOrPawn = 0;
// Store last move information
lastMove = {
from: {
row: fromSquare.row,
col: fromSquare.col
},
to: {
row: toSquare.row,
col: toSquare.col
},
piece: piece.type,
color: piece.color,
isCapture: true,
isTrap: true
};
return; // Skip the rest of the move logic
}
}
// Handle capture
if (isCapture) {
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Move the piece
var capturedPiece = toSquare.piece; // Store captured piece before overwriting
fromSquare.setPiece(null);
toSquare.setPiece(piece);
// Mark piece as moved (for castling and pawn double move)
piece.hasMoved = true;
// Capture the King Rule: Check if a king was captured
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing' && capturedPiece && capturedPiece.type === 'king') {
gameState = 'checkmate'; // Use checkmate state to signify win
endGame('King Captured! ' + piece.color + ' wins!');
return; // End the move function early
}
// Handle pawn promotion
if (piece.type === 'pawn' && (toSquare.row === 0 || toSquare.row === 7)) {
promotePawn(toSquare);
}
// Update move counter for 50-move rule
if (isPawnMove || isCapture) {
movesSinceCaptureOrPawn = 0;
} else {
movesSinceCaptureOrPawn++;
}
// Store last move information
lastMove = {
from: {
row: fromSquare.row,
col: fromSquare.col
},
to: {
row: toSquare.row,
col: toSquare.col
},
piece: piece.type,
color: piece.color,
isCapture: isCapture
};
// --- Special Mode Post-Move Actions ---
// Transformo-fest: Transform piece after moving (if not king)
if (game.gameMode === 'specialGame' && game.specialRule === 'transformoFest' && piece.type !== 'king') {
var possibleTypes = ['pawn', 'rook', 'knight', 'bishop', 'queen'];
var randomType = possibleTypes[Math.floor(Math.random() * possibleTypes.length)];
toSquare.setPiece(new ChessPiece(randomType, piece.color));
// Add a small visual indicator? Maybe flash?
LK.effects.flashObject(toSquare.piece, 0xFFFF00, 300);
}
// Icy Day: Chance to freeze moved piece (if not king or yeti)
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay' && piece.type !== 'king' && !piece.isYeti) {
if (Math.random() < ICY_DAY_FREEZE_CHANCE) {
// Check if already frozen
var alreadyFrozen = false;
for (var i = 0; i < icyDayFrozenPieces.length; i++) {
if (icyDayFrozenPieces[i].row === toSquare.row && icyDayFrozenPieces[i].col === toSquare.col) {
alreadyFrozen = true;
break;
}
}
// Freeze if not already frozen
if (!alreadyFrozen) {
icyDayFrozenPieces.push({
row: toSquare.row,
col: toSquare.col,
turnsLeft: ICY_DAY_FREEZE_TURNS
});
if (toSquare.piece) {
// Check if piece still exists (could have transformed?)
toSquare.piece.tint = 0x00AAFF; // Blue tint for frozen
toSquare.piece.isFrozenIcy = true; // Mark as frozen
}
// Optional: Add notification text
var freezeText = new Text2("FROZEN!", {
size: 30,
fill: 0x00AAFF
});
freezeText.anchor.set(0.5, 1.0);
freezeText.x = toSquare.x + SQUARE_SIZE / 2;
freezeText.y = toSquare.y; // Position above the square
game.addChild(freezeText);
LK.setTimeout(function () {
game.removeChild(freezeText);
}, 1000);
}
}
}
// Increment GOAT-ed turn counter after white moves
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge' && piece.color === 'white') {
goatedTurnCounter++;
}
}
// Promote a pawn to a queen
function promotePawn(square) {
var piece = square.piece;
square.setPiece(new ChessPiece('queen', piece.color));
LK.getSound('promotion').play();
}
// Clear highlights from all squares
function clearHighlights() {
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
if (board[row][col]) {
board[row][col].highlight();
}
}
}
}
// Calculate valid moves for a piece
function calculateValidMoves(square) {
var piece = square.piece;
var moves = [];
if (!piece) {
return moves;
}
var row = square.row;
var col = square.col;
// Check for special movement rules first
// GOAT-ed Challenge: Permanently goatified white pieces move like kings
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge' && piece.color === 'white' && piece.isPermanentlyGoatified) {
calculateKingMoves(row, col, piece, moves);
}
// Dragon Boss: Dragon piece moves like Queen + Knight
else if (game.gameMode === 'specialGame' && game.specialRule === 'dragonBoss' && piece.isDragon) {
calculateRookMoves(row, col, piece, moves); // Queen part 1
calculateBishopMoves(row, col, piece, moves); // Queen part 2
calculateKnightMoves(row, col, piece, moves); // Knight part
}
// Check if piece is temporarily goatified (Crazy Mode)
else if (game.gameMode === 'crazy' && piece.color === 'white' && crazyAbilities.isPieceGoatified(row, col)) {
// Goatified pieces move like kings
calculateKingMoves(row, col, piece, moves);
} else {
// Regular move calculation based on piece type (apply special rules within these)
switch (piece.type) {
case 'pawn':
calculatePawnMoves(row, col, piece, moves);
break;
case 'rook':
calculateRookMoves(row, col, piece, moves);
break;
case 'knight':
calculateKnightMoves(row, col, piece, moves);
break;
case 'bishop':
calculateBishopMoves(row, col, piece, moves);
break;
case 'queen':
calculateRookMoves(row, col, piece, moves);
calculateBishopMoves(row, col, piece, moves);
break;
case 'king':
calculateKingMoves(row, col, piece, moves);
break;
}
}
// Filter out moves that would put the king in check
return filterCheckMoves(square, moves);
}
// Calculate valid moves for a pawn
function calculatePawnMoves(row, col, piece, moves) {
var direction = piece.color === 'white' ? -1 : 1;
// Move forward one square
if (isValidSquare(row + direction, col) && !board[row + direction][col].piece) {
moves.push({
row: row + direction,
col: col
});
// Move forward two squares from starting position
if (!piece.hasMoved && isValidSquare(row + 2 * direction, col) && !board[row + 2 * direction][col].piece) {
moves.push({
row: row + 2 * direction,
col: col
});
}
}
// Captures
var captureDirections = [{
row: direction,
col: -1
}, {
row: direction,
col: 1
}];
for (var i = 0; i < captureDirections.length; i++) {
var dir = captureDirections[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
if (isValidSquare(newRow, newCol) && board[newRow][newCol].piece && board[newRow][newCol].piece.color !== piece.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
// Backwards Pawns Rule
if (game.gameMode === 'specialGame' && game.specialRule === 'backwardsPawns') {
var backwardDirection = -direction; // Opposite direction
// Move backward one square
if (isValidSquare(row + backwardDirection, col) && !board[row + backwardDirection][col].piece) {
moves.push({
row: row + backwardDirection,
col: col
});
// No double move backwards
}
// Backward captures
var backwardCaptureDirections = [{
row: backwardDirection,
col: -1
}, {
row: backwardDirection,
col: 1
}];
for (var i = 0; i < backwardCaptureDirections.length; i++) {
var bDir = backwardCaptureDirections[i];
var bNewRow = row + bDir.row;
var bNewCol = col + bDir.col;
if (isValidSquare(bNewRow, bNewCol) && board[bNewRow][bNewCol].piece && board[bNewRow][bNewCol].piece.color !== piece.color) {
moves.push({
row: bNewRow,
col: bNewCol
});
}
}
}
}
// Calculate valid moves for a rook
function calculateRookMoves(row, col, piece, moves) {
var directions = [{
row: -1,
col: 0
},
// up
{
row: 1,
col: 0
},
// down
{
row: 0,
col: -1
},
// left
{
row: 0,
col: 1
} // right
];
calculateSlidingMoves(row, col, piece, moves, directions);
}
// Calculate valid moves for a bishop
function calculateBishopMoves(row, col, piece, moves) {
var directions = [{
row: -1,
col: -1
},
// up-left
{
row: -1,
col: 1
},
// up-right
{
row: 1,
col: -1
},
// down-left
{
row: 1,
col: 1
} // down-right
];
var diagonalMoves = []; // Store initial diagonal moves before filtering/adding horizontal
calculateSlidingMoves(row, col, piece, moves, directions);
// SHREDR Bishops: Add horizontal move option
if (game.gameMode === 'specialGame' && game.specialRule === 'shredrBishops') {
var shredrMoves = [];
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
shredrMoves.push(move); // Keep the original diagonal move
// Add horizontal moves from the landing square
var landingRow = move.row;
var landingCol = move.col;
// Check horizontal left
if (isValidSquare(landingRow, landingCol - 1)) {
var leftSquare = board[landingRow][landingCol - 1];
if (!leftSquare || !leftSquare.piece || leftSquare.piece.color !== piece.color) {
shredrMoves.push({
row: landingRow,
col: landingCol - 1
});
}
}
// Check horizontal right
if (isValidSquare(landingRow, landingCol + 1)) {
var rightSquare = board[landingRow][landingCol + 1];
if (!rightSquare || !rightSquare.piece || rightSquare.piece.color !== piece.color) {
shredrMoves.push({
row: landingRow,
col: landingCol + 1
});
}
}
}
// Replace original moves with shredr moves (includes original + horizontal)
// Need to clear original moves and add shredrMoves back.
// The 'moves' array is passed by reference, modify it directly.
moves.length = 0; // Clear the array
for (var k = 0; k < shredrMoves.length; k++) {
// Avoid duplicates (simple check, might need improvement for complex scenarios)
var exists = false;
for (var j = 0; j < moves.length; j++) {
if (moves[j].row === shredrMoves[k].row && moves[j].col === shredrMoves[k].col) {
exists = true;
break;
}
}
if (!exists) {
moves.push(shredrMoves[k]);
}
}
}
}
// Calculate valid moves for sliding pieces (rook, bishop, queen)
function calculateSlidingMoves(row, col, piece, moves, directions) {
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
while (isValidSquare(newRow, newCol)) {
var targetSquare = board[newRow][newCol];
if (!targetSquare.piece) {
// Empty square, can move here
moves.push({
row: newRow,
col: newCol
});
} else {
// Found a piece
if (targetSquare.piece.color !== piece.color) {
// Can capture opponent's piece
moves.push({
row: newRow,
col: newCol
});
}
// SHREDR Bishops can jump, so don't break here for them
if (!(game.gameMode === 'specialGame' && game.specialRule === 'shredrBishops' && piece.type === 'bishop')) {
// Can't move past a piece (unless SHREDR bishop)
break;
}
}
newRow += dir.row;
newCol += dir.col;
}
}
}
// Calculate valid moves for a knight
function calculateKnightMoves(row, col, piece, moves) {
var knightDirections = [{
row: -2,
col: -1
}, {
row: -2,
col: 1
}, {
row: -1,
col: -2
}, {
row: -1,
col: 2
}, {
row: 1,
col: -2
}, {
row: 1,
col: 2
}, {
row: 2,
col: -1
}, {
row: 2,
col: 1
}];
for (var i = 0; i < knightDirections.length; i++) {
var dir = knightDirections[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
if (isValidSquare(newRow, newCol)) {
var targetSquare = board[newRow][newCol];
if (!targetSquare.piece || targetSquare.piece.color !== piece.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// Calculate valid moves for a king
function calculateKingMoves(row, col, piece, moves) {
var kingDirections = [{
row: -1,
col: -1
}, {
row: -1,
col: 0
}, {
row: -1,
col: 1
}, {
row: 0,
col: -1
}, {
row: 0,
col: 1
}, {
row: 1,
col: -1
}, {
row: 1,
col: 0
}, {
row: 1,
col: 1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
if (isValidSquare(newRow, newCol)) {
var targetSquare = board[newRow][newCol];
if (!targetSquare.piece || targetSquare.piece.color !== piece.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// Castling
if (!piece.hasMoved) {
// Kingside castling
if (canCastle(row, col, 0, 3)) {
moves.push({
row: row,
col: col + 2
});
}
// Queenside castling
if (canCastle(row, col, 0, -4)) {
moves.push({
row: row,
col: col - 2
});
}
}
}
// Check if castling is possible
function canCastle(row, col, rowOffset, colOffset) {
var rookCol = colOffset > 0 ? 7 : 0;
var rookRow = row;
// Check if there's a rook in the corner
var rookSquare = board[rookRow][rookCol];
if (!rookSquare.piece || rookSquare.piece.type !== 'rook' || rookSquare.piece.hasMoved) {
return false;
}
// Check if there are pieces between the king and the rook
var startCol = Math.min(col, rookCol) + 1;
var endCol = Math.max(col, rookCol) - 1;
for (var c = startCol; c <= endCol; c++) {
if (board[row][c].piece) {
return false;
}
}
// Check if the king would move through or into check
var direction = colOffset > 0 ? 1 : -1;
if (isSquareAttacked(row, col, otherPlayer(currentPlayer)) || isSquareAttacked(row, col + direction, otherPlayer(currentPlayer)) || isSquareAttacked(row, col + 2 * direction, otherPlayer(currentPlayer))) {
return false;
}
return true;
}
// Filter out moves that would put the king in check
function filterCheckMoves(fromSquare, moves) {
var validMoves = [];
var piece = fromSquare.piece;
// Capture the King Rule: King doesn't care about checks, skip filtering
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing') {
// Return all moves unfiltered for check
for (var i = 0; i < moves.length; i++) {
validMoves.push(moves[i]);
}
return validMoves;
}
// Standard check filtering
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
// Make sure the move exists and has valid row/col properties
if (!move || !board[move.row] || !board[move.row][move.col]) {
continue;
}
// Simulate the move
var originalPiece = board[move.row][move.col].piece;
board[move.row][move.col].piece = piece;
board[fromSquare.row][fromSquare.col].piece = null;
// Check if the king is in check after the move
var inCheck = isKingInCheck(piece.color);
// Undo the move
board[fromSquare.row][fromSquare.col].piece = piece;
board[move.row][move.col].piece = originalPiece;
if (!inCheck) {
validMoves.push(move);
}
}
return validMoves;
}
// Check if a square is valid (within the board)
function isValidSquare(row, col) {
// Use dynamic board sizes and column limits
return row >= 0 && row < BOARD_SIZE_Y && col >= BOARD_START_COL && col <= BOARD_END_COL;
}
// Check if the king is in check
function isKingInCheck(color) {
// Find the king
var kingSquare = findKing(color);
if (!kingSquare) {
return false;
}
// Check if the king is attacked by any opponent's piece
return isSquareAttacked(kingSquare.row, kingSquare.col, otherPlayer(color));
}
// Check if a square is attacked by any piece of the given color
function isSquareAttacked(row, col, attackerColor) {
for (var r = 0; r < BOARD_SIZE; r++) {
for (var c = 0; c < BOARD_SIZE; c++) {
var square = board[r][c];
if (square.piece && square.piece.color === attackerColor) {
var moves = [];
switch (square.piece.type) {
case 'pawn':
// Pawns attack diagonally
var direction = square.piece.color === 'white' ? -1 : 1;
var attackDirections = [{
row: direction,
col: -1
}, {
row: direction,
col: 1
}];
for (var i = 0; i < attackDirections.length; i++) {
var dir = attackDirections[i];
var attackRow = r + dir.row;
var attackCol = c + dir.col;
if (attackRow === row && attackCol === col) {
return true;
}
}
break;
case 'rook':
calculateRookMoves(r, c, square.piece, moves);
break;
case 'knight':
calculateKnightMoves(r, c, square.piece, moves);
break;
case 'bishop':
calculateBishopMoves(r, c, square.piece, moves);
break;
case 'queen':
calculateRookMoves(r, c, square.piece, moves);
calculateBishopMoves(r, c, square.piece, moves);
break;
case 'king':
var kingDirections = [{
row: -1,
col: -1
}, {
row: -1,
col: 0
}, {
row: -1,
col: 1
}, {
row: 0,
col: -1
}, {
row: 0,
col: 1
}, {
row: 1,
col: -1
}, {
row: 1,
col: 0
}, {
row: 1,
col: 1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var attackRow = r + dir.row;
var attackCol = c + dir.col;
if (attackRow === row && attackCol === col) {
return true;
}
}
break;
}
// Check if the square is in the list of moves
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
if (move.row === row && move.col === col) {
return true;
}
}
}
}
}
return false;
}
// Find the king of the given color
function findKing(color) {
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.type === 'king' && square.piece.color === color) {
return {
row: row,
col: col
};
}
}
}
return null;
}
// Get the other player's color
function otherPlayer(color) {
return color === 'white' ? 'black' : 'white';
}
// Switch the current player
function switchPlayer() {
currentPlayer = otherPlayer(currentPlayer);
// Icy Day: Update frozen piece counters at the start of the next player's turn
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay') {
updateIcyDayFrozenPieces();
}
}
// Helper function to update Icy Day frozen pieces
function updateIcyDayFrozenPieces() {
for (var i = icyDayFrozenPieces.length - 1; i >= 0; i--) {
var frozen = icyDayFrozenPieces[i];
frozen.turnsLeft--;
if (frozen.turnsLeft <= 0) {
var square = board[frozen.row][frozen.col];
// Unfreeze if the piece is still there and marked as frozen
if (square && square.piece && square.piece.isFrozenIcy) {
// Reset tint based on whether it's a Yeti or regular piece
if (square.piece.isYeti) {
square.piece.tint = 0xADD8E6; // Yeti tint
} else {
square.piece.tint = 0xFFFFFF; // Default tint
}
square.piece.isFrozenIcy = false; // Unmark
}
icyDayFrozenPieces.splice(i, 1); // Remove from list
}
}
}
// Check the game state (check, checkmate, stalemate)
function checkGameState() {
// Capture the King Rule: Check/Checkmate logic is different
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing') {
// Stalemate can still occur if no legal moves (excluding king capture scenario handled in movePiece)
var allMovesCtK = getAllPossibleMoves(currentPlayer);
if (allMovesCtK.length === 0) {
// Stalemate if no moves and king not captured
gameState = 'stalemate'; //{eT} // Reuse stalemate code line ID if appropriate
endGame('Draw by stalemate!');
return;
}
// Check for 50-move rule - REMOVED
/*
if (movesSinceCaptureOrPawn >= 50) { //{gH} // Keep line ID comment for history
gameState = 'stalemate';
endGame('Draw by 50-move rule!');
return;
}
*/
// Check for insufficient material still applies
if (hasInsufficientMaterial()) {
gameState = 'stalemate';
endGame('Draw by insufficient material!');
return;
}
// No check state in this mode
gameState = 'playing'; //{eV} // Reuse playing state code line ID
return; // Skip standard check/checkmate logic
}
// GOAT-ed Challenge: Check for survival win
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge') {
if (goatedTurnCounter >= 25) {
gameState = 'checkmate'; // Use checkmate state to signify player win
endGame('Survived 25 turns! You win!');
return;
}
// Check if AI king is captured (AI loses) - should not happen if AI plays normally
var blackKing = findKing('black');
if (!blackKing) {
gameState = 'checkmate';
endGame('AI King lost! You win!');
return;
}
// Check if player king is captured (Player loses)
var whiteKing = findKing('white');
if (!whiteKing) {
gameState = 'checkmate'; // Player lost
endGame('Your King was captured! AI wins!');
return;
}
}
// Dragon Boss: Check if Dragon is captured (Player wins)
if (game.gameMode === 'specialGame' && game.specialRule === 'dragonBoss') {
var dragonFound = false;
for (var r = 0; r < BOARD_SIZE_Y; r++) {
for (var c = BOARD_START_COL; c <= BOARD_END_COL; c++) {
if (board[r][c] && board[r][c].piece && board[r][c].piece.isDragon) {
dragonFound = true;
break;
}
}
if (dragonFound) {
break;
}
}
if (!dragonFound) {
gameState = 'checkmate'; // Player wins
endGame('Dragon Captured! You win!');
return;
}
// Check if player king is captured (Player loses)
var whiteKingDragon = findKing('white');
if (!whiteKingDragon) {
gameState = 'checkmate'; // Player lost
endGame('Your King was captured! Dragon wins!');
return;
}
}
// --- Standard Check/Checkmate/Stalemate Logic ---
// Check if the current player's king is in check
var inCheck = isKingInCheck(currentPlayer);
// Get all possible moves for the current player
var allMoves = getAllPossibleMoves(currentPlayer);
// Check for 50-move rule - REMOVED
/*
if (movesSinceCaptureOrPawn >= 50) { //{hh} // Keep line ID comment for history
gameState = 'stalemate';
endGame('Draw by 50-move rule!');
return;
}
*/
// Check for insufficient material
if (hasInsufficientMaterial()) {
gameState = 'stalemate';
endGame('Draw by insufficient material!');
return;
}
// Check crazy mode king only frozen situation
if (game.gameMode === 'crazy' && isKingOnlyFrozen()) {
gameState = 'stalemate';
endGame('Draw! King is frozen and alone.');
return;
}
if (inCheck) {
if (allMoves.length === 0) {
// Checkmate
gameState = 'checkmate';
endGame('Checkmate! ' + otherPlayer(currentPlayer) + ' wins!');
} else {
// Check
gameState = 'check';
LK.getSound('check').play();
}
} else if (allMoves.length === 0) {
// Stalemate
gameState = 'stalemate';
endGame('Draw by stalemate!');
} else {
gameState = 'playing';
}
}
// Get all possible moves for a player
function getAllPossibleMoves(color) {
var allMoves = [];
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === color) {
var moves = calculateValidMoves(square);
for (var i = 0; i < moves.length; i++) {
allMoves.push({
fromRow: row,
fromCol: col,
toRow: moves[i].row,
toCol: moves[i].col
});
}
}
}
}
return allMoves;
}
// Check for insufficient material (K vs K, K vs KB, K vs KN)
function hasInsufficientMaterial() {
var pieces = {
white: {
count: 0,
bishop: 0,
knight: 0
},
black: {
count: 0,
bishop: 0,
knight: 0
}
};
// Count all pieces on the board
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece) {
var piece = square.piece;
pieces[piece.color].count++;
if (piece.type === 'bishop') {
pieces[piece.color].bishop++;
} else if (piece.type === 'knight') {
pieces[piece.color].knight++;
} else if (piece.type !== 'king') {
// If there's any other piece (pawn, rook, queen), material is sufficient
if (piece.type === 'pawn' || piece.type === 'rook' || piece.type === 'queen') {
return false;
}
}
}
}
}
// K vs K
if (pieces.white.count === 1 && pieces.black.count === 1) {
return true;
}
// K vs KB or K vs KN
if (pieces.white.count === 1 && pieces.black.count === 2 && (pieces.black.bishop === 1 || pieces.black.knight === 1) || pieces.black.count === 1 && pieces.white.count === 2 && (pieces.white.bishop === 1 || pieces.white.knight === 1)) {
return true;
}
return false;
}
// Check for king-only frozen situation in crazy mode
function isKingOnlyFrozen() {
if (game.gameMode !== 'crazy') {
return false;
}
var whiteCount = 0;
var whiteKingRow = -1;
var whiteKingCol = -1;
var blackCount = 0;
// Count pieces and find white king
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece) {
if (square.piece.color === 'white') {
whiteCount++;
if (square.piece.type === 'king') {
whiteKingRow = row;
whiteKingCol = col;
}
} else {
blackCount++;
}
}
}
}
// If white only has the king and it's frozen
return whiteCount === 1 && whiteKingRow !== -1 && crazyAbilities.isPieceFrozen(whiteKingRow, whiteKingCol);
}
// End the game
function endGame(message) {
var gameOverText = new Text2(message, {
size: 60,
fill: 0xFFFFFF
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 2048 / 2;
gameOverText.y = 200;
game.addChild(gameOverText);
// Update score based on winner
if (gameState === 'checkmate') {
if (currentPlayer === 'black') {
// White wins
LK.setScore(100);
} else {
// Black wins (AI wins)
LK.setScore(0);
}
} else {
// Draw
LK.setScore(50);
}
// Show game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
// Make an AI move
function makeAIMove() {
if (gameState === 'checkmate' || gameState === 'stalemate') {
aiThinking = false;
return;
}
// Get all possible moves for the AI
var possibleMoves = getAllPossibleMoves('black');
if (possibleMoves.length === 0) {
aiThinking = false;
return;
}
// Simple AI: pick a random move
var randomMove = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
// Make the move
var fromSquare = board[randomMove.fromRow][randomMove.fromCol];
var toSquare = board[randomMove.toRow][randomMove.toCol];
// Highlight the selected square
fromSquare.highlight('selected');
// Wait a bit before making the move
LK.setTimeout(function () {
// Move the piece
movePiece(fromSquare, toSquare);
// Clear highlights
clearHighlights();
// Check if the move ended the game (e.g., king capture)
// gameState might have been updated in movePiece
if (gameState === 'checkmate' || gameState === 'stalemate') {
aiThinking = false;
return; // Exit if movePiece ended the game
}
// Use crazy ability ONLY in crazy mode
if (game.gameMode === 'crazy' && Math.random() < crazyAbilityChance) {
// Use ability after a short delay
LK.setTimeout(function () {
crazyAbilities.useRandomAbility();
// Note: Ability might change game state, but we check again after switch anyway
}, 500);
}
// Switch back to player
switchPlayer(); //{iS} // currentPlayer is now 'white'
// Check for check/checkmate/stalemate for the player whose turn it now is
checkGameState(); //{iR} // Checks state for 'white'
// If game ended after check
if (gameState === 'checkmate' || gameState === 'stalemate') {
aiThinking = false;
return;
}
// Update crazy mode frozen and goatified pieces ONLY in crazy mode
if (game.gameMode === 'crazy') {
crazyAbilities.updateFrozenPieces();
crazyAbilities.updateGoatifiedPieces();
}
// AI turn is over
aiThinking = false;
}, 500);
}
// Create score display
var scoreTxt = new Text2('Crazy Chess', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create turn display
var turnText = new Text2('Your Turn', {
size: 40,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.y = 70;
LK.gui.top.addChild(turnText);
// Game update function
game.update = function () {
// If in title screen, update title screen
if (gameState === 'title' && titleScreen) {
titleScreen.update();
return;
}
// If in game mode selection screen, update that screen
if (gameState === 'modeSelect' && gameModeScreen) {
gameModeScreen.update();
return;
}
// If in bot selection screen, update that screen
if (gameState === 'botSelect' && botSelectionScreen) {
botSelectionScreen.update();
return;
}
// If in daily challenge screen, update that screen
if (gameState === 'dailyChallenge' && dailyChallengeScreen) {
dailyChallengeScreen.update();
return; // Stop further updates if in this screen
}
// Fog of War visual update (placeholder)
if (game.gameMode === 'specialGame' && game.specialRule === 'fogOfWar') {
// updateFogOfWar(); // TODO: Implement visual fog logic
}
// Update UI Text for Special Modes
if (game.gameMode === 'specialGame') {
if (game.specialRule === 'goatedChallenge') {
scoreTxt.setText('GOAT-ed Challenge');
turnText.setText('Turn: ' + goatedTurnCounter + '/25');
if (gameState === 'checkmate' && goatedTurnCounter >= 25) {
turnText.setText('You Win!');
} else if (gameState === 'checkmate') {
turnText.setText('AI Wins!'); // Player lost before 25 turns
} else if (aiThinking) {
turnText.setText('AI Thinking...');
}
// Don't overwrite win/loss text immediately
return; // Skip default turn text logic for this mode if not thinking
} else if (game.specialRule === 'dragonBoss') {
scoreTxt.setText('Dragon Boss');
if (gameState === 'checkmate') {
var whiteKingDragon = findKing('white');
turnText.setText(whiteKingDragon ? 'You Win!' : 'Dragon Wins!');
} else if (aiThinking) {
turnText.setText('Dragon Thinking...');
} else {
turnText.setText('Your Turn');
}
return; // Skip default turn text logic
} else if (game.specialRule === 'icyDay') {
scoreTxt.setText('Icy Day');
// Use default turn text logic below
} else if (game.specialRule === 'transformoFest') {
scoreTxt.setText('Transformo-fest');
// Use default turn text logic below
}
// Add other special rule title updates here if needed
}
// Update turn text (Default logic, handles standard game and some special modes)
if (gameState === 'checkmate') {
// Capture the King uses 'checkmate' state for win, adjust text
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing') {
// Winner determined in endGame, turnText might be showing winner already
} else if (currentPlayer === 'black') {
turnText.setText('You Win!');
} else {
turnText.setText('AI Wins!');
}
} else if (gameState === 'stalemate') {
turnText.setText('Draw!');
} else if (aiThinking) {
turnText.setText('AI Thinking...');
} else {
turnText.setText(currentPlayer === 'white' ? 'Your Turn' : 'AI Turn');
}
};
// Method to show the game mode selection screen
game.showGameModeScreen = function () {
// Remove title screen if it exists
if (titleScreen) {
game.removeChild(titleScreen);
titleScreen = null;
}
// Remove game mode screen if it already exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null; // Clear reference
}
// Remove bot selection screen if it exists
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Remove daily challenge screen if it exists
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Create and add game mode screen
gameModeScreen = new GameModeScreen();
gameModeScreen.x = 2048 / 2;
gameModeScreen.y = 2732 / 2;
game.addChild(gameModeScreen);
// Update game state
gameState = 'modeSelect';
};
// Method to show the title screen
// Method to show the bot selection screen
game.showBotSelectionScreen = function () {
// Remove game mode screen if it exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
// Remove title screen just in case (shouldn't happen in normal flow)
if (titleScreen) {
game.removeChild(titleScreen);
titleScreen = null;
}
// Remove existing bot selection screen if present
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Create and add bot selection screen
botSelectionScreen = new BotSelectionScreen();
botSelectionScreen.x = 2048 / 2;
botSelectionScreen.y = 2732 / 2;
game.addChild(botSelectionScreen);
// Update game state
gameState = 'botSelect';
};
// Method to show the daily challenge screen
game.showDailyChallengeScreen = function () {
// Remove other screens
if (titleScreen) {
game.removeChild(titleScreen);
titleScreen = null;
}
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Create and add daily challenge screen
dailyChallengeScreen = new DailyChallengeScreen();
dailyChallengeScreen.x = 2048 / 2;
dailyChallengeScreen.y = 2732 / 2;
game.addChild(dailyChallengeScreen);
// Update game state
gameState = 'dailyChallenge';
};
game.showTitleScreen = function () {
// Remove game mode screen if it exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
// Remove bot selection screen if it exists
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Remove daily challenge screen if it exists
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Create and add title screen
titleScreen = new TitleScreen();
titleScreen.x = 2048 / 2;
titleScreen.y = 2732 / 2;
game.addChild(titleScreen);
// Update game state
gameState = 'title';
};
// Method to start the game from mode selection screen
game.startGame = function () {
// Remove game mode screen if it exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
// Remove bot selection screen if it exists
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Remove daily challenge screen if it exists (shouldn't be possible to start game from here, but for safety)
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Change state based on mode
if (game.gameMode === 'specialGame') {
gameState = 'specialGame'; // Or keep as 'playing' and rely on game.specialRule
scoreTxt.setText('Daily Challenge - ' + selectedBot.toUpperCase()); // Or show special rule name?
} else {
gameState = 'playing';
// Update game title based on selected mode
scoreTxt.setText('Crazy Chess - ' + game.gameMode.toUpperCase());
}
// Initialize the chess board
initializeBoard();
// Reset special mode variables
goatedTurnCounter = 0;
icyDayFrozenPieces = [];
yetiPawns = [];
// Show the score and turn text
scoreTxt.visible = true;
turnText.visible = true;
};
// Show title screen first
titleScreen = new TitleScreen();
titleScreen.x = 2048 / 2;
titleScreen.y = 2732 / 2;
game.addChild(titleScreen);
// Hide score and turn text initially
scoreTxt.visible = false;
turnText.visible = false;
; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BotSelectionScreen = Container.expand(function () {
var self = Container.call(this);
// Bot descriptions and icons
var botData = {
Martin: {
description: 'Martin makes many bad moves.',
icon: 'martinIcon'
},
Lina: {
description: 'Lina has an offensive strategy.',
icon: 'linaIcon'
},
Carl: {
description: 'Carl tries to stall the game.',
icon: 'carlIcon'
},
Wizard: {
description: 'Wizard uses magic for great moves.',
icon: 'wizardIcon'
},
King: {
description: 'King plays optimally but lacks a Queen.',
icon: 'kingIcon'
}
};
var bots = ['Martin', 'Lina', 'Carl', 'Wizard', 'King'];
// Assume these assets ('martinIcon', 'linaIcon', etc.) are defined or will be auto-created by the engine.
// Example sizes used here are 80x80.
var startY = -350; // Adjusted start Y for more spacing overall
var spacingY = 180; // Increased vertical spacing between buttons
// Create title text
var titleText = new Text2('Select Your Opponent', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = startY - 100; // Position title above the first button container
self.addChild(titleText);
// Store button containers and backgrounds for interaction setup
var buttonContainers = [];
var buttonBackgrounds = [];
// Create bot buttons with icons and descriptions using Containers
for (var i = 0; i < bots.length; i++) {
var botName = bots[i];
var currentBotData = botData[botName];
// Create a container for each button entry
var buttonContainer = new Container();
buttonContainer.x = 0; // Center container horizontally relative to parent (BotSelectionScreen)
buttonContainer.y = startY + i * spacingY; // Position container vertically
self.addChild(buttonContainer);
buttonContainers.push(buttonContainer); // Store the container
// Get icon asset
var icon = LK.getAsset(currentBotData.icon, {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80
});
icon.x = -200; // Position icon significantly to the left within the container
icon.y = 0; // Center icon vertically within the container
// Get bot name text asset
var botButtonText = new Text2(botName.toUpperCase(), {
size: 60,
fill: 0xFFFFFF
});
botButtonText.anchor.set(0, 0.5); // Anchor text to its left-middle
botButtonText.x = icon.x + icon.width / 2 + 20; // Position text right of the icon center + padding
botButtonText.y = -15; // Position text slightly above the vertical center
// Get description text asset
var descriptionText = new Text2(currentBotData.description, {
size: 30,
fill: 0xCCCCCC
});
descriptionText.anchor.set(0, 0.5); // Anchor text to its left-middle
descriptionText.x = botButtonText.x; // Align description start with bot name start
descriptionText.y = 15; // Position description below the vertical center
// Calculate dynamic width/height needed for the background based on content
var textBlockWidth = Math.max(botButtonText.width, descriptionText.width);
var contentWidth = icon.width + 20 + textBlockWidth; // Icon width + padding + max text width
var contentHeight = Math.max(icon.height, botButtonText.height + descriptionText.height + 10); // Max of icon height or combined text height + spacing
var bgWidth = contentWidth + 80; // Add horizontal padding to content width
var bgHeight = contentHeight + 40; // Add vertical padding to content height
// Create background asset
var bg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: bgWidth,
height: bgHeight,
tint: 0x3a3a3a
});
bg.alpha = 0.7;
// Center the background horizontally around the content block
bg.x = icon.x - icon.width / 2 + contentWidth / 2;
bg.y = 0; // Center background vertically in the container
buttonContainer.addChild(bg); // Add background first so text/icon are on top
buttonBackgrounds.push(bg); // Store background reference for interaction effects
// Add icon and text elements on top of the background
buttonContainer.addChild(icon);
buttonContainer.addChild(botButtonText);
buttonContainer.addChild(descriptionText);
// Make the entire container interactive
buttonContainer.interactive = true;
buttonContainer.botIndex = i; // Store the bot index on the container for the click handler
}
// Create Back button text
var backButton = new Text2('< BACK', {
size: 60,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
// Position back button below the last bot container, accounting for increased spacing
backButton.y = startY + bots.length * spacingY + 60;
self.addChild(backButton);
// Create Back button background
var backBg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: backButton.width + 80,
height: backButton.height + 40,
tint: 0x3a3a3a
});
backBg.alpha = 0.7;
backBg.x = backButton.x; // Align background center with text center
backBg.y = backButton.y; // Align background center with text center
self.addChild(backBg); // Add background first
self.addChild(backButton); // Add text on top of background
// --- Interaction Setup ---
// Bot button interactions (using stored containers and backgrounds)
for (var i = 0; i < buttonContainers.length; i++) {
(function (index) {
var btnContainer = buttonContainers[index];
var bg = buttonBackgrounds[index]; // Get the corresponding background
// Attach down event to the container
btnContainer.down = function (x, y, obj) {
// Flash the background alpha on click
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
// Return alpha to normal after flash
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Handle button action: select bot and start game
game.selectedBot = bots[index]; // Use stored index to get bot name
game.startGame();
};
})(i);
}
// Back button interaction
(function () {
var btn = backButton; // Reference to the Text2 object
var bg = backBg; // Reference to the background shape
btn.interactive = true; // Make the Text2 object interactive
// Attach down event to the text object
btn.down = function (x, y, obj) {
// Flash the background alpha on click
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
// Return alpha to normal after flash
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Handle back button action: show the previous screen
game.showGameModeScreen();
};
})();
// Update function (can be used for animations later if needed)
self.update = function () {
// Optional: Add animations like pulsing if needed
};
return self;
});
var ChessPiece = Container.expand(function (type, color) {
var self = Container.call(this);
self.type = type;
self.color = color;
self.hasMoved = false;
var pieceId = color + type.charAt(0).toUpperCase() + type.slice(1);
var pieceGraphics = self.attachAsset(pieceId, {
anchorX: 0.5,
anchorY: 0.5
});
var label;
switch (type) {
case 'pawn':
label = '';
break;
case 'rook':
label = 'R';
break;
case 'knight':
label = 'N';
break;
case 'bishop':
label = 'B';
break;
case 'queen':
label = 'Q';
break;
case 'king':
label = 'K';
break;
}
if (label) {
var text = new Text2(label, {
size: 40,
fill: color === 'white' ? "#000000" : "#ffffff"
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
}
return self;
});
var ChessSquare = Container.expand(function (row, col, color) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.color = color;
self.piece = null;
var squareGraphics = self.attachAsset(color === 'white' ? 'whiteSquare' : 'blackSquare', {
anchorX: 0,
anchorY: 0
});
self.highlight = function (type) {
if (type === 'selected') {
squareGraphics.tint = 0x829769;
} else if (type === 'validMove') {
squareGraphics.tint = 0xcdd26a;
} else {
// Reset to original color
squareGraphics.tint = color === 'white' ? 0xf0d9b5 : 0xb58863;
}
};
self.setPiece = function (piece) {
if (self.piece) {
self.removeChild(self.piece);
}
self.piece = piece;
if (piece) {
self.addChild(piece);
piece.x = squareGraphics.width / 2;
piece.y = squareGraphics.height / 2;
}
};
self.down = function (x, y, obj) {
game.selectSquare(self);
};
return self;
});
var CrazyAbility = Container.expand(function () {
var self = Container.call(this);
// Available abilities
self.abilities = [{
name: 'Freeze',
description: 'Freezes a white piece for 2 turns'
}, {
name: 'Instant Promotion',
description: 'Promotes a black pawn to a powerful piece'
}, {
name: 'Stampede',
description: 'Removes 3 random pieces from the board'
}, {
name: 'Summon',
description: 'Summons a black rook on an empty square'
}, {
name: 'Teleport',
description: 'Teleports one of your pieces to danger!'
}, {
name: 'Goatify',
description: 'Turns a piece into a goat that moves like a king for 2 turns'
}, {
name: 'Trap',
description: 'Places a bear trap that captures pieces that land on it'
}];
// Stores pieces that are frozen
self.frozenPieces = [];
// Use a random ability
self.useRandomAbility = function () {
var randomIndex = Math.floor(Math.random() * self.abilities.length);
var ability = self.abilities[randomIndex];
// Show ability notification
self.showAbilityNotification(ability.name);
// Execute ability
switch (ability.name) {
case 'Freeze':
self.freezeRandomPiece();
break;
case 'Instant Promotion':
self.instantPromotion();
break;
case 'Stampede':
self.stampede();
break;
case 'Summon':
self.summonRook();
break;
case 'Teleport':
self.teleport();
break;
case 'Goatify':
self.goatifyPiece();
break;
case 'Trap':
self.placeTrap();
break;
}
};
// Show notification of ability used
self.showAbilityNotification = function (abilityName) {
var notification = new Text2('AI USED: ' + abilityName + '!', {
size: 50,
fill: 0xFF0000
});
notification.anchor.set(0.5, 0.5);
notification.x = 2048 / 2;
notification.y = 2732 / 2 - 300;
game.addChild(notification);
// Flash effect
LK.effects.flashObject(notification, 0xFF0000, 500);
// Remove after delay
LK.setTimeout(function () {
game.removeChild(notification);
}, 2000);
};
// Freeze a random white piece for 2 turns
self.freezeRandomPiece = function () {
var whitePieces = [];
// Find all white pieces
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'white') {
whitePieces.push({
square: square,
row: row,
col: col
});
}
}
}
if (whitePieces.length > 0) {
// Select random white piece
var randomIndex = Math.floor(Math.random() * whitePieces.length);
var targetPiece = whitePieces[randomIndex];
// Add to frozen list with 2 turns duration
self.frozenPieces.push({
row: targetPiece.row,
col: targetPiece.col,
turnsLeft: 2
});
// Visual effect - tint blue
targetPiece.square.piece.tint = 0x00AAFF;
}
};
// Turn a random black pawn into a queen, bishop or rook
self.instantPromotion = function () {
var blackPawns = [];
// Find all black pawns
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'black' && square.piece.type === 'pawn') {
blackPawns.push({
square: square
});
}
}
}
if (blackPawns.length > 0) {
// Select random black pawn
var randomIndex = Math.floor(Math.random() * blackPawns.length);
var targetPawn = blackPawns[randomIndex];
// Choose random promotion type
var promotionTypes = ['queen', 'bishop', 'rook'];
var randomType = promotionTypes[Math.floor(Math.random() * promotionTypes.length)];
// Promote the pawn
targetPawn.square.setPiece(new ChessPiece(randomType, 'black'));
LK.getSound('promotion').play();
}
};
// Remove 3 random pieces from the board
self.stampede = function () {
var allPieces = [];
// Find all pieces, excluding all kings
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.type !== 'king') {
allPieces.push({
square: square
});
}
}
}
// Remove up to 3 random pieces
var removeCount = Math.min(3, allPieces.length);
for (var i = 0; i < removeCount; i++) {
if (allPieces.length > 0) {
var randomIndex = Math.floor(Math.random() * allPieces.length);
var targetPiece = allPieces[randomIndex];
// Remove piece
targetPiece.square.setPiece(null);
// Remove from array
allPieces.splice(randomIndex, 1);
}
}
LK.getSound('capture').play();
};
// Summon a black rook on a random empty square
self.summonRook = function () {
var emptySquares = [];
// Find all empty squares
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (!square.piece) {
emptySquares.push({
square: square
});
}
}
}
if (emptySquares.length > 0) {
// Select random empty square
var randomIndex = Math.floor(Math.random() * emptySquares.length);
var targetSquare = emptySquares[randomIndex];
// Add new rook
targetSquare.square.setPiece(new ChessPiece('rook', 'black'));
}
};
// Decrement frozen piece counters
self.updateFrozenPieces = function () {
for (var i = self.frozenPieces.length - 1; i >= 0; i--) {
self.frozenPieces[i].turnsLeft--;
// If no turns left, unfreeze the piece
if (self.frozenPieces[i].turnsLeft <= 0) {
var row = self.frozenPieces[i].row;
var col = self.frozenPieces[i].col;
// Only unfreeze if piece still exists at that position
if (board[row][col].piece && board[row][col].piece.color === 'white') {
board[row][col].piece.tint = 0xFFFFFF;
}
self.frozenPieces.splice(i, 1);
}
}
};
// Teleport a white piece to a dangerous position
self.teleport = function () {
var whitePieces = [];
var dangerSquares = [];
// Find all white pieces (except king)
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'white' && square.piece.type !== 'king') {
whitePieces.push({
square: square,
row: row,
col: col
});
}
}
}
// Find all squares under attack by black pieces
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
// Check if this square is attacked by any black piece
if (isSquareAttacked(row, col, 'black') && !board[row][col].piece) {
dangerSquares.push({
row: row,
col: col
});
}
}
}
// If we have white pieces and danger squares
if (whitePieces.length > 0 && dangerSquares.length > 0) {
// Select random white piece
var randomPieceIndex = Math.floor(Math.random() * whitePieces.length);
var targetPiece = whitePieces[randomPieceIndex];
// Select random danger square
var randomSquareIndex = Math.floor(Math.random() * dangerSquares.length);
var dangerSquare = dangerSquares[randomSquareIndex];
// Teleport the piece
var piece = targetPiece.square.piece;
targetPiece.square.setPiece(null);
board[dangerSquare.row][dangerSquare.col].setPiece(piece);
// Visual effect - flash the teleported piece
LK.effects.flashObject(board[dangerSquare.row][dangerSquare.col].piece, 0xFF00FF, 500);
}
};
// Stores goatified pieces data
self.goatifiedPieces = [];
// Turn a white piece into a goat (moves like king)
self.goatifyPiece = function () {
var whitePieces = [];
// Find all white pieces except the king (king is immune)
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === 'white' && square.piece.type !== 'king') {
whitePieces.push({
square: square,
row: row,
col: col,
type: square.piece.type
});
}
}
}
if (whitePieces.length > 0) {
// Select random white piece
var randomIndex = Math.floor(Math.random() * whitePieces.length);
var targetPiece = whitePieces[randomIndex];
// Add to goatified list with 2 turns duration
self.goatifiedPieces.push({
row: targetPiece.row,
col: targetPiece.col,
originalType: targetPiece.type,
turnsLeft: 2
});
// Visual effect - tint piece to goat color
targetPiece.square.piece.tint = 0x88BB33;
// Show a goat indicator
var goatText = new Text2("π", {
size: 30,
fill: 0xFFFFFF
});
goatText.anchor.set(0.5, 0.5);
goatText.x = targetPiece.square.x + SQUARE_SIZE / 2;
goatText.y = targetPiece.square.y + SQUARE_SIZE / 2 - 30;
game.addChild(goatText);
// Remove indicator after a short time
LK.setTimeout(function () {
game.removeChild(goatText);
}, 1500);
}
};
// Array to track trap positions
self.trapSquares = [];
// Place a bear trap on a random empty square
self.placeTrap = function () {
var emptySquares = [];
// Find all empty squares
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (!square.piece) {
emptySquares.push({
row: row,
col: col
});
}
}
}
if (emptySquares.length > 0) {
// Select random empty square
var randomIndex = Math.floor(Math.random() * emptySquares.length);
var trapSquare = emptySquares[randomIndex];
// Add trap to the list
self.trapSquares.push({
row: trapSquare.row,
col: trapSquare.col
});
// Visual indication of trap
var trapIndicator = new Text2("β οΈ", {
size: 40,
fill: 0xFF5500
});
trapIndicator.anchor.set(0.5, 0.5);
trapIndicator.x = board[trapSquare.row][trapSquare.col].x + SQUARE_SIZE / 2;
trapIndicator.y = board[trapSquare.row][trapSquare.col].y + SQUARE_SIZE / 2;
game.addChild(trapIndicator);
board[trapSquare.row][trapSquare.col].trapIndicator = trapIndicator;
}
};
// Update goatified pieces counters
self.updateGoatifiedPieces = function () {
for (var i = self.goatifiedPieces.length - 1; i >= 0; i--) {
self.goatifiedPieces[i].turnsLeft--;
// If no turns left, restore the piece to its original type
if (self.goatifiedPieces[i].turnsLeft <= 0) {
var row = self.goatifiedPieces[i].row;
var col = self.goatifiedPieces[i].col;
var originalType = self.goatifiedPieces[i].originalType;
// Only restore if piece still exists at that position and is white
if (board[row][col].piece && board[row][col].piece.color === 'white') {
// Reset tint
board[row][col].piece.tint = 0xFFFFFF;
}
self.goatifiedPieces.splice(i, 1);
}
}
};
// Check if a piece is goatified
self.isPieceGoatified = function (row, col) {
for (var i = 0; i < self.goatifiedPieces.length; i++) {
if (self.goatifiedPieces[i].row === row && self.goatifiedPieces[i].col === col) {
return true;
}
}
return false;
};
// Check if a square has a trap
self.hasTrap = function (row, col) {
for (var i = 0; i < self.trapSquares.length; i++) {
if (self.trapSquares[i].row === row && self.trapSquares[i].col === col) {
return true;
}
}
return false;
};
// Trigger trap on a square and remove it
self.triggerTrap = function (row, col) {
for (var i = 0; i < self.trapSquares.length; i++) {
if (self.trapSquares[i].row === row && self.trapSquares[i].col === col) {
// Remove the trap indicator
if (board[row][col].trapIndicator) {
game.removeChild(board[row][col].trapIndicator);
board[row][col].trapIndicator = null;
}
// Remove the trap from our list
self.trapSquares.splice(i, 1);
return true;
}
}
return false;
};
// Check if a piece is frozen
self.isPieceFrozen = function (row, col) {
for (var i = 0; i < self.frozenPieces.length; i++) {
if (self.frozenPieces[i].row === row && self.frozenPieces[i].col === col) {
return true;
}
}
return false;
};
return self;
});
var DailyChallengeScreen = Container.expand(function () {
var self = Container.call(this);
// Special rules collection - now the only focus of daily challenges
var specialRules = [{
name: 'Fog of War',
description: 'You can only see squares your pieces can move to.',
id: 'fogOfWar'
}, {
name: 'Backwards Pawns',
description: 'Pawns can move and capture backwards.',
id: 'backwardsPawns'
}, {
name: 'Capture the King',
description: 'No check/checkmate. Capture the enemy king to win!',
id: 'captureKing'
}, {
name: 'SHREDR Bishops',
description: 'Bishops can jump over pieces and move one square horizontally.',
id: 'shredrBishops'
}, {
name: 'Small Chess',
description: 'Play on a 4x8 board without knights or bishops.',
id: 'smallChess'
}, {
name: 'Transformo-fest',
description: 'Pieces (except kings) randomly transform after moving!',
id: 'transformoFest'
}, {
name: 'Dragon Boss',
description: 'Capture the Dragon (Queen+Knight moves) using 3 Knights & 4 Bishops.',
id: 'dragonBoss'
}, {
name: 'GOAT-ed Challenge',
description: 'Your pieces (except king) are goats! Survive 25 turns.',
id: 'goatedChallenge'
}, {
name: 'Icy Day',
description: 'Pieces may freeze after moving! Some pawns are immune Yetis.',
id: 'icyDay'
}];
// Select a random special rule for the "day"
var selectedSpecialRule = specialRules[Math.floor(Math.random() * specialRules.length)];
// --- UI Elements ---
// Title Text
var titleText = new Text2('Daily Challenge: Special Game', {
size: 70,
fill: 0xFFFFFF,
wordWrap: true,
// Enable word wrap
wordWrapWidth: 1800 // Set max width before wrapping
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -350; // Position higher up
self.addChild(titleText);
// Compile description for the selected special rule
var finalDescription = 'Special Rule: ' + selectedSpecialRule.name + '\n' + selectedSpecialRule.description;
// Description Text
var descriptionText = new Text2(finalDescription, {
size: 45,
fill: 0xCCCCCC,
align: 'center',
// Center align text
wordWrap: true,
// Enable word wrap
wordWrapWidth: 1600 // Set max width before wrapping
});
descriptionText.anchor.set(0.5, 0.5);
descriptionText.y = -150; // Position below title
self.addChild(descriptionText);
// Start Challenge Button
var startButton = new Text2('START CHALLENGE', {
size: 70,
fill: 0xFFFFFF
});
startButton.anchor.set(0.5, 0.5);
startButton.y = 100;
self.addChild(startButton);
// Back Button
var backButton = new Text2('< BACK', {
size: 60,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
backButton.y = 300; // Position further down
self.addChild(backButton);
// Add backgrounds for buttons
var buttons = [startButton, backButton];
var buttonBackgrounds = [];
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var bg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: btn.width + 80,
height: btn.height + 40,
tint: 0x3a3a3a
});
bg.alpha = 0.7;
bg.x = btn.x;
bg.y = btn.y;
buttonBackgrounds.push(bg);
self.addChild(bg);
self.addChild(btn); // Re-add button to put it on top
}
// --- Interactions ---
// Start Button Interaction
(function (index) {
var btn = buttons[index];
var bg = buttonBackgrounds[index];
btn.interactive = true;
btn.down = function (x, y, obj) {
// Flash effect
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Set the game mode and the specific rule
game.gameMode = 'specialGame';
game.specialRule = selectedSpecialRule.id; // Store the ID of the rule
// Remove this screen
if (game.dailyChallengeScreen) {
game.removeChild(game.dailyChallengeScreen);
game.dailyChallengeScreen = null;
}
game.showBotSelectionScreen(); // Go to bot selection for special game
};
})(0); // Index 0 is the start button
// Back Button Interaction
(function (index) {
var btn = buttons[index];
var bg = buttonBackgrounds[index];
btn.interactive = true;
btn.down = function (x, y, obj) {
// Flash effect
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Go back to the game mode selection screen
game.showGameModeScreen();
};
})(1); // Index 1 is the back button
self.update = function () {
// Optional: Add animations if needed
};
return self;
});
var GameModeScreen = Container.expand(function () {
var self = Container.call(this);
// Create title text
var titleText = new Text2('Select Game Mode', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -250;
self.addChild(titleText);
// Create mode buttons
var classicButton = new Text2('CLASSIC', {
size: 80,
fill: 0xFFFFFF
});
classicButton.anchor.set(0.5, 0.5);
classicButton.y = -150;
self.addChild(classicButton);
var crazyButton = new Text2('CRAZY MODE', {
size: 80,
fill: 0xFFFFFF
});
crazyButton.anchor.set(0.5, 0.5);
crazyButton.y = 0;
self.addChild(crazyButton);
var dailyChallengeButton = new Text2('DAILY CHALLENGE', {
size: 80,
fill: 0xFFFFFF
});
dailyChallengeButton.anchor.set(0.5, 0.5);
dailyChallengeButton.y = 150;
self.addChild(dailyChallengeButton);
var backButton = new Text2('< BACK', {
size: 60,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
backButton.y = 350;
self.addChild(backButton);
// Add highlighting for buttons
var buttons = [classicButton, crazyButton, dailyChallengeButton, backButton];
var buttonBackgrounds = [];
// Create button backgrounds
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var bg = LK.getAsset('blackSquare', {
anchorX: 0.5,
anchorY: 0.5,
width: btn.width + 80,
height: btn.height + 40,
tint: 0x3a3a3a
});
bg.alpha = 0.7;
bg.x = btn.x;
bg.y = btn.y;
buttonBackgrounds.push(bg);
self.addChild(bg);
self.addChild(btn); // Re-add button to put it on top
}
// Add button hover effects
for (var i = 0; i < buttons.length; i++) {
(function (index) {
var btn = buttons[index];
var bg = buttonBackgrounds[index];
btn.interactive = true;
btn.down = function (x, y, obj) {
// Flash button when clicked
tween(bg, {
alpha: 1
}, {
duration: 100,
onFinish: function onComplete() {
tween(bg, {
alpha: 0.7
}, {
duration: 100
});
}
});
// Handle button action
if (index === 0) {
// Classic mode
game.gameMode = 'classic';
game.showBotSelectionScreen(); // Go to bot selection
} else if (index === 1) {
// Crazy mode
game.gameMode = 'crazy';
game.showBotSelectionScreen(); // Go to bot selection
} else if (index === 2) {
// Daily Challenge mode
game.gameMode = 'dailyChallenge';
game.showDailyChallengeScreen(); // Show the dedicated screen
} else if (index === 3) {
// Back button
game.showTitleScreen();
}
};
})(i);
}
// Pulse animation for the first button
var pulseScale = 1;
var increasing = true;
self.update = function () {
// Pulse effect for the Classic button
if (increasing) {
pulseScale += 0.002;
if (pulseScale >= 1.05) {
increasing = false;
}
} else {
pulseScale -= 0.002;
if (pulseScale <= 0.95) {
increasing = true;
}
}
classicButton.scale.set(pulseScale);
buttonBackgrounds[0].scale.set(pulseScale);
};
return self;
});
var TitleScreen = Container.expand(function () {
var self = Container.call(this);
// Create title text
var titleText = new Text2('Crazy Chess', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
self.addChild(titleText);
// Create start button
var startButton = new Text2('TAP TO START', {
size: 90,
fill: 0xFFFFFF
});
startButton.anchor.set(0.5, 0.5);
startButton.y = 250;
self.addChild(startButton);
// Add pulsing animation to the start button
var pulseScale = 1;
var increasing = true;
self.update = function () {
// Pulse effect for start button
if (increasing) {
pulseScale += 0.003;
if (pulseScale >= 1.1) {
increasing = false;
}
} else {
pulseScale -= 0.003;
if (pulseScale <= 0.9) {
increasing = true;
}
}
startButton.scale.set(pulseScale);
};
// Handle tap anywhere to start
self.down = function () {
game.showGameModeScreen();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game constants - Default to 8x8
var BOARD_SIZE = 8; // Default board size
var BOARD_SIZE_X = 8;
var BOARD_SIZE_Y = 8;
var SQUARE_SIZE = 100;
var BOARD_OFFSET_X = (2048 - BOARD_SIZE_X * SQUARE_SIZE) / 2;
var BOARD_OFFSET_Y = (2732 - BOARD_SIZE_Y * SQUARE_SIZE) / 2;
var BOARD_START_COL = 0; // For small chess adjustment
var BOARD_END_COL = 7; // For small chess adjustment
// Game state variables
var board = [];
var selectedSquare = null;
var validMoves = [];
var currentPlayer = 'white';
var gameState = 'title'; // 'title', 'modeSelect', 'botSelect', 'playing', 'check', 'checkmate', 'stalemate', 'dailyChallenge', 'specialGame'
var specialRule = null; // Stores the active special rule for daily challenges
var aiThinking = false;
var titleScreen = null;
var gameModeScreen = null;
var botSelectionScreen = null; // Add variable for the bot selection screen
var dailyChallengeScreen = null; // Add variable for the daily challenge screen
var selectedBot = ''; // Add variable to store the selected bot
// gameMode is now set on the game object, not as a separate variable
var crazyAbilities = new CrazyAbility();
var crazyAbilityChance = 0.4; // 40% chance of AI using ability after a move
// Special Mode Variables
var goatedTurnCounter = 0;
var ICY_DAY_FREEZE_CHANCE = 0.3; // 30% chance to freeze
var ICY_DAY_FREEZE_TURNS = 2; // Freeze duration
var icyDayFrozenPieces = []; // Stores { row, col, turnsLeft } for Icy Day
var yetiPawns = []; // Stores { row, col } of Yeti pawns
// Variables for draw conditions
var movesSinceCaptureOrPawn = 0; // For 50-move rule
var lastMove = null; // To track piece captures and pawn moves
// Initialize the chess board
function initializeBoard() {
board = [];
// Adjust board size and offsets for Small Chess
if (game.gameMode === 'specialGame' && game.specialRule === 'smallChess') {
BOARD_SIZE_X = 4;
BOARD_SIZE_Y = 8;
BOARD_START_COL = 2;
BOARD_END_COL = 5;
BOARD_OFFSET_X = (2048 - BOARD_SIZE_X * SQUARE_SIZE) / 2;
BOARD_OFFSET_Y = (2732 - BOARD_SIZE_Y * SQUARE_SIZE) / 2;
} else {
// Reset to default if not small chess
BOARD_SIZE_X = 8;
BOARD_SIZE_Y = 8;
BOARD_START_COL = 0;
BOARD_END_COL = 7;
BOARD_OFFSET_X = (2048 - BOARD_SIZE_X * SQUARE_SIZE) / 2;
BOARD_OFFSET_Y = (2732 - BOARD_SIZE_Y * SQUARE_SIZE) / 2;
}
// Create the chess board based on dimensions
for (var row = 0; row < BOARD_SIZE_Y; row++) {
board[row] = [];
for (var col = 0; col < BOARD_SIZE_X; col++) {
var boardCol = col + BOARD_START_COL; // Map visual column to board array column
var squareColor = (row + boardCol) % 2 === 0 ? 'white' : 'black';
var square = new ChessSquare(row, boardCol, squareColor); // Store the actual board column
square.x = BOARD_OFFSET_X + col * SQUARE_SIZE; // Visual position based on 0 to BOARD_SIZE_X-1
square.y = BOARD_OFFSET_Y + row * SQUARE_SIZE;
// Initialize board array slots, only add visible squares to game
board[row][boardCol] = square; // Use actual board column index
game.addChild(square);
}
// Initialize non-visible columns for internal logic if needed (e.g. full 8x8 array)
if (game.gameMode !== 'specialGame' || game.specialRule !== 'smallChess') {
// Standard 8x8 initialization already covered
} else {
// For small chess, fill the rest of the 8x8 array rows with null or placeholder if needed by logic
// This example assumes logic primarily uses BOARD_START_COL and BOARD_END_COL checks
for (var r = 0; r < BOARD_SIZE_Y; ++r) {
for (var c = 0; c < 8; ++c) {
if (c < BOARD_START_COL || c > BOARD_END_COL) {
if (!board[r]) {
board[r] = [];
}
board[r][c] = null; // Or a placeholder indicating off-board
}
}
}
}
}
// Place the pieces on the board
placePieces();
}
// Place all chess pieces in their starting positions
function placePieces() {
if (game.gameMode === 'specialGame' && game.specialRule === 'smallChess') {
// Small Chess Setup (4x8 board, cols 2-5)
// Place pawns
for (var col = BOARD_START_COL; col <= BOARD_END_COL; col++) {
board[1][col].setPiece(new ChessPiece('pawn', 'black'));
board[6][col].setPiece(new ChessPiece('pawn', 'white'));
}
// Place rooks
board[0][BOARD_START_COL].setPiece(new ChessPiece('rook', 'black')); // Col 2
board[0][BOARD_END_COL].setPiece(new ChessPiece('rook', 'black')); // Col 5
board[7][BOARD_START_COL].setPiece(new ChessPiece('rook', 'white')); // Col 2
board[7][BOARD_END_COL].setPiece(new ChessPiece('rook', 'white')); // Col 5
// Place queens
board[0][BOARD_START_COL + 1].setPiece(new ChessPiece('queen', 'black')); // Col 3
board[7][BOARD_START_COL + 1].setPiece(new ChessPiece('queen', 'white')); // Col 3
// Place kings
board[0][BOARD_START_COL + 2].setPiece(new ChessPiece('king', 'black')); // Col 4
board[7][BOARD_START_COL + 2].setPiece(new ChessPiece('king', 'white')); // Col 4
// No knights or bishops
} else if (game.gameMode === 'specialGame' && game.specialRule === 'dragonBoss') {
// Dragon Boss Setup
// White: King, 4 Bishops, 3 Knights
board[7][4].setPiece(new ChessPiece('king', 'white')); // e1
board[7][2].setPiece(new ChessPiece('bishop', 'white')); // c1
board[7][3].setPiece(new ChessPiece('bishop', 'white')); // d1
board[7][5].setPiece(new ChessPiece('bishop', 'white')); // f1
board[7][6].setPiece(new ChessPiece('bishop', 'white')); // g1
board[7][1].setPiece(new ChessPiece('knight', 'white')); // b1
board[7][7].setPiece(new ChessPiece('knight', 'white')); // h1
board[6][4].setPiece(new ChessPiece('knight', 'white')); // e2 - One knight forward
// Black: Dragon (use Queen asset for now, moves handled separately)
board[0][4].setPiece(new ChessPiece('queen', 'black')); // Dragon on e8
board[0][4].piece.isDragon = true; // Mark as Dragon
} else {
// Standard Setup
// Place pawns
for (var col = 0; col < 8; col++) {
//{8q} // Use 8 explicitly
board[1][col].setPiece(new ChessPiece('pawn', 'black'));
board[6][col].setPiece(new ChessPiece('pawn', 'white'));
}
// Place rooks
board[0][0].setPiece(new ChessPiece('rook', 'black'));
board[0][7].setPiece(new ChessPiece('rook', 'black'));
board[7][0].setPiece(new ChessPiece('rook', 'white'));
board[7][7].setPiece(new ChessPiece('rook', 'white'));
// Place knights
board[0][1].setPiece(new ChessPiece('knight', 'black'));
board[0][6].setPiece(new ChessPiece('knight', 'black'));
board[7][1].setPiece(new ChessPiece('knight', 'white'));
board[7][6].setPiece(new ChessPiece('knight', 'white'));
// Place bishops
board[0][2].setPiece(new ChessPiece('bishop', 'black'));
board[0][5].setPiece(new ChessPiece('bishop', 'black'));
board[7][2].setPiece(new ChessPiece('bishop', 'white'));
board[7][5].setPiece(new ChessPiece('bishop', 'white'));
// Place queens
board[0][3].setPiece(new ChessPiece('queen', 'black'));
board[7][3].setPiece(new ChessPiece('queen', 'white'));
// Place kings
board[0][4].setPiece(new ChessPiece('king', 'black'));
board[7][4].setPiece(new ChessPiece('king', 'white'));
}
// GOAT-ed Challenge: Make all white pieces (except king) goatified
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge') {
for (var r = 0; r < 8; r++) {
for (var c = 0; c < 8; c++) {
if (board[r][c] && board[r][c].piece && board[r][c].piece.color === 'white' && board[r][c].piece.type !== 'king') {
board[r][c].piece.isPermanentlyGoatified = true;
board[r][c].piece.tint = 0x88BB33; // Goat color tint
}
}
}
goatedTurnCounter = 0; // Reset turn counter
}
// Icy Day: Designate some white pawns as Yetis
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay') {
icyDayFrozenPieces = []; // Reset frozen list
yetiPawns = []; // Reset yeti list
// Make pawns on b, d, e, g Yetis
var yetiCols = [1, 3, 4, 6]; // b, d, e, g columns
for (var i = 0; i < yetiCols.length; i++) {
var col = yetiCols[i];
if (board[6][col] && board[6][col].piece && board[6][col].piece.type === 'pawn') {
board[6][col].piece.isYeti = true;
board[6][col].piece.tint = 0xADD8E6; // Light blue tint for Yetis
yetiPawns.push({
row: 6,
col: col
});
}
}
}
// Special rule for 'King' bot: remove the black queen
if (game.selectedBot === 'King' && game.gameMode !== 'specialGame') {
// Don't apply if it's a special game
if (board[0][3].piece && board[0][3].piece.type === 'queen' && board[0][3].piece.color === 'black') {
board[0][3].setPiece(null);
}
}
}
// Select a square on the chess board
game.selectSquare = function (square) {
// If AI is thinking, don't allow moves
if (aiThinking || gameState === 'checkmate' || gameState === 'stalemate') {
return;
}
// If the current player is not the human player, don't allow moves
if (currentPlayer !== 'white') {
return;
}
// Check if piece is frozen (Only in Crazy Mode)
if (game.gameMode === 'crazy' && square.piece && square.piece.color === 'white' && crazyAbilities.isPieceFrozen(square.row, square.col)) {
// Show notification that piece is frozen
var notification = new Text2('This piece is frozen!', {
size: 40,
fill: 0x00AAFF
});
notification.anchor.set(0.5, 0.5);
notification.x = 2048 / 2;
notification.y = 2732 / 2 - 200;
game.addChild(notification);
// Remove notification after delay
LK.setTimeout(function () {
game.removeChild(notification);
}, 1500);
return;
}
// Check if piece is frozen (Icy Day Mode)
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay' && square.piece && square.piece.isFrozenIcy) {
// Show notification that piece is frozen
var notificationIcy = new Text2('This piece is frozen!', {
size: 40,
//{90} // Reuse ID if appropriate
fill: 0x00AAFF
}); //{91} // Reuse ID if appropriate
notificationIcy.anchor.set(0.5, 0.5); //{92} // Reuse ID if appropriate
notificationIcy.x = 2048 / 2; //{93} // Reuse ID if appropriate
notificationIcy.y = 2732 / 2 - 200;
game.addChild(notificationIcy); //{94} // Reuse ID if appropriate
// Remove notification after delay
LK.setTimeout(function () {
//{95} // Reuse ID if appropriate
game.removeChild(notificationIcy); //{96} // Reuse ID if appropriate
}, 1500); //{97} // Reuse ID if appropriate
return; // Don't allow selection
}
// Clear previous selections
clearHighlights();
// If a square is already selected and the new square is a valid move
if (selectedSquare && isValidMove(square)) {
// Move the piece
var from = selectedSquare;
var to = square;
movePiece(from, to);
// Reset selection state
selectedSquare = null;
validMoves = [];
clearHighlights(); // Clear highlights after move
// Check if the move ended the game via specific rules (like king capture)
// gameState might have been updated in movePiece
if (gameState === 'checkmate' || gameState === 'stalemate') {
// endGame was already called in movePiece
return;
}
// Switch to the next player
switchPlayer(); //{9C} // currentPlayer is now the opponent ('black')
// Now check the state for the player whose turn it just became
checkGameState(); //{9B} // Checks state for 'black'
// If the game ended due to checkmate/stalemate after the switch
if (gameState === 'checkmate' || gameState === 'stalemate') {
// Game end message already shown by checkGameState -> endGame
return;
}
// If it's AI's turn (now black) and game is still going
if (currentPlayer === 'black') {
aiThinking = true;
LK.setTimeout(makeAIMove, 1000);
}
}
// If clicking on a piece of the current player
else if (square.piece && square.piece.color === currentPlayer) {
// Select the square
selectedSquare = square;
square.highlight('selected');
// Calculate valid moves
validMoves = calculateValidMoves(square);
// Highlight valid moves
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
board[move.row][move.col].highlight('validMove');
}
}
};
// Check if a move to the target square is valid
function isValidMove(targetSquare) {
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (move.row === targetSquare.row && move.col === targetSquare.col) {
return true;
}
}
return false;
}
// Move a piece from one square to another
function movePiece(fromSquare, toSquare) {
var piece = fromSquare.piece;
// Check for pawn move or capture (for 50-move rule)
var isPawnMove = piece.type === 'pawn';
var isCapture = toSquare.piece !== null;
// Check for trap only in crazy mode
var trapTriggered = false;
if (game.gameMode === 'crazy' && crazyAbilities.hasTrap(toSquare.row, toSquare.col)) {
// Trigger trap effect
trapTriggered = crazyAbilities.triggerTrap(toSquare.row, toSquare.col);
// If trap is triggered and the piece is white (not king - king is immune)
if (trapTriggered && piece.color === 'white' && piece.type !== 'king') {
// Show trap activation message
var trapMessage = new Text2("TRAP TRIGGERED!", {
size: 50,
fill: 0xFF0000
});
trapMessage.anchor.set(0.5, 0.5);
trapMessage.x = 2048 / 2;
trapMessage.y = 2732 / 2 - 250;
game.addChild(trapMessage);
// Remove message after delay
LK.setTimeout(function () {
game.removeChild(trapMessage);
}, 1500);
// Piece is captured by the trap
fromSquare.setPiece(null);
LK.getSound('capture').play();
// Update move counter for 50-move rule
movesSinceCaptureOrPawn = 0;
// Store last move information
lastMove = {
from: {
row: fromSquare.row,
col: fromSquare.col
},
to: {
row: toSquare.row,
col: toSquare.col
},
piece: piece.type,
color: piece.color,
isCapture: true,
isTrap: true
};
return; // Skip the rest of the move logic
}
}
// Handle capture
if (isCapture) {
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Move the piece
var capturedPiece = toSquare.piece; // Store captured piece before overwriting
fromSquare.setPiece(null);
toSquare.setPiece(piece);
// Mark piece as moved (for castling and pawn double move)
piece.hasMoved = true;
// Capture the King Rule: Check if a king was captured
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing' && capturedPiece && capturedPiece.type === 'king') {
gameState = 'checkmate'; // Use checkmate state to signify win
endGame('King Captured! ' + piece.color + ' wins!');
return; // End the move function early
}
// Handle pawn promotion
if (piece.type === 'pawn' && (toSquare.row === 0 || toSquare.row === 7)) {
promotePawn(toSquare);
}
// Update move counter for 50-move rule
if (isPawnMove || isCapture) {
movesSinceCaptureOrPawn = 0;
} else {
movesSinceCaptureOrPawn++;
}
// Store last move information
lastMove = {
from: {
row: fromSquare.row,
col: fromSquare.col
},
to: {
row: toSquare.row,
col: toSquare.col
},
piece: piece.type,
color: piece.color,
isCapture: isCapture
};
// --- Special Mode Post-Move Actions ---
// Transformo-fest: Transform piece after moving (if not king)
if (game.gameMode === 'specialGame' && game.specialRule === 'transformoFest' && piece.type !== 'king') {
var possibleTypes = ['pawn', 'rook', 'knight', 'bishop', 'queen'];
var randomType = possibleTypes[Math.floor(Math.random() * possibleTypes.length)];
toSquare.setPiece(new ChessPiece(randomType, piece.color));
// Add a small visual indicator? Maybe flash?
LK.effects.flashObject(toSquare.piece, 0xFFFF00, 300);
}
// Icy Day: Chance to freeze moved piece (if not king or yeti)
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay' && piece.type !== 'king' && !piece.isYeti) {
if (Math.random() < ICY_DAY_FREEZE_CHANCE) {
// Check if already frozen
var alreadyFrozen = false;
for (var i = 0; i < icyDayFrozenPieces.length; i++) {
if (icyDayFrozenPieces[i].row === toSquare.row && icyDayFrozenPieces[i].col === toSquare.col) {
alreadyFrozen = true;
break;
}
}
// Freeze if not already frozen
if (!alreadyFrozen) {
icyDayFrozenPieces.push({
row: toSquare.row,
col: toSquare.col,
turnsLeft: ICY_DAY_FREEZE_TURNS
});
if (toSquare.piece) {
// Check if piece still exists (could have transformed?)
toSquare.piece.tint = 0x00AAFF; // Blue tint for frozen
toSquare.piece.isFrozenIcy = true; // Mark as frozen
}
// Optional: Add notification text
var freezeText = new Text2("FROZEN!", {
size: 30,
fill: 0x00AAFF
});
freezeText.anchor.set(0.5, 1.0);
freezeText.x = toSquare.x + SQUARE_SIZE / 2;
freezeText.y = toSquare.y; // Position above the square
game.addChild(freezeText);
LK.setTimeout(function () {
game.removeChild(freezeText);
}, 1000);
}
}
}
// Increment GOAT-ed turn counter after white moves
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge' && piece.color === 'white') {
goatedTurnCounter++;
}
}
// Promote a pawn to a queen
function promotePawn(square) {
var piece = square.piece;
square.setPiece(new ChessPiece('queen', piece.color));
LK.getSound('promotion').play();
}
// Clear highlights from all squares
function clearHighlights() {
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
if (board[row][col]) {
board[row][col].highlight();
}
}
}
}
// Calculate valid moves for a piece
function calculateValidMoves(square) {
var piece = square.piece;
var moves = [];
if (!piece) {
return moves;
}
var row = square.row;
var col = square.col;
// Check for special movement rules first
// GOAT-ed Challenge: Permanently goatified white pieces move like kings
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge' && piece.color === 'white' && piece.isPermanentlyGoatified) {
calculateKingMoves(row, col, piece, moves);
}
// Dragon Boss: Dragon piece moves like Queen + Knight
else if (game.gameMode === 'specialGame' && game.specialRule === 'dragonBoss' && piece.isDragon) {
calculateRookMoves(row, col, piece, moves); // Queen part 1
calculateBishopMoves(row, col, piece, moves); // Queen part 2
calculateKnightMoves(row, col, piece, moves); // Knight part
}
// Check if piece is temporarily goatified (Crazy Mode)
else if (game.gameMode === 'crazy' && piece.color === 'white' && crazyAbilities.isPieceGoatified(row, col)) {
// Goatified pieces move like kings
calculateKingMoves(row, col, piece, moves);
} else {
// Regular move calculation based on piece type (apply special rules within these)
switch (piece.type) {
case 'pawn':
calculatePawnMoves(row, col, piece, moves);
break;
case 'rook':
calculateRookMoves(row, col, piece, moves);
break;
case 'knight':
calculateKnightMoves(row, col, piece, moves);
break;
case 'bishop':
calculateBishopMoves(row, col, piece, moves);
break;
case 'queen':
calculateRookMoves(row, col, piece, moves);
calculateBishopMoves(row, col, piece, moves);
break;
case 'king':
calculateKingMoves(row, col, piece, moves);
break;
}
}
// Filter out moves that would put the king in check
return filterCheckMoves(square, moves);
}
// Calculate valid moves for a pawn
function calculatePawnMoves(row, col, piece, moves) {
var direction = piece.color === 'white' ? -1 : 1;
// Move forward one square
if (isValidSquare(row + direction, col) && !board[row + direction][col].piece) {
moves.push({
row: row + direction,
col: col
});
// Move forward two squares from starting position
if (!piece.hasMoved && isValidSquare(row + 2 * direction, col) && !board[row + 2 * direction][col].piece) {
moves.push({
row: row + 2 * direction,
col: col
});
}
}
// Captures
var captureDirections = [{
row: direction,
col: -1
}, {
row: direction,
col: 1
}];
for (var i = 0; i < captureDirections.length; i++) {
var dir = captureDirections[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
if (isValidSquare(newRow, newCol) && board[newRow][newCol].piece && board[newRow][newCol].piece.color !== piece.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
// Backwards Pawns Rule
if (game.gameMode === 'specialGame' && game.specialRule === 'backwardsPawns') {
var backwardDirection = -direction; // Opposite direction
// Move backward one square
if (isValidSquare(row + backwardDirection, col) && !board[row + backwardDirection][col].piece) {
moves.push({
row: row + backwardDirection,
col: col
});
// No double move backwards
}
// Backward captures
var backwardCaptureDirections = [{
row: backwardDirection,
col: -1
}, {
row: backwardDirection,
col: 1
}];
for (var i = 0; i < backwardCaptureDirections.length; i++) {
var bDir = backwardCaptureDirections[i];
var bNewRow = row + bDir.row;
var bNewCol = col + bDir.col;
if (isValidSquare(bNewRow, bNewCol) && board[bNewRow][bNewCol].piece && board[bNewRow][bNewCol].piece.color !== piece.color) {
moves.push({
row: bNewRow,
col: bNewCol
});
}
}
}
}
// Calculate valid moves for a rook
function calculateRookMoves(row, col, piece, moves) {
var directions = [{
row: -1,
col: 0
},
// up
{
row: 1,
col: 0
},
// down
{
row: 0,
col: -1
},
// left
{
row: 0,
col: 1
} // right
];
calculateSlidingMoves(row, col, piece, moves, directions);
}
// Calculate valid moves for a bishop
function calculateBishopMoves(row, col, piece, moves) {
var directions = [{
row: -1,
col: -1
},
// up-left
{
row: -1,
col: 1
},
// up-right
{
row: 1,
col: -1
},
// down-left
{
row: 1,
col: 1
} // down-right
];
var diagonalMoves = []; // Store initial diagonal moves before filtering/adding horizontal
calculateSlidingMoves(row, col, piece, moves, directions);
// SHREDR Bishops: Add horizontal move option
if (game.gameMode === 'specialGame' && game.specialRule === 'shredrBishops') {
var shredrMoves = [];
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
shredrMoves.push(move); // Keep the original diagonal move
// Add horizontal moves from the landing square
var landingRow = move.row;
var landingCol = move.col;
// Check horizontal left
if (isValidSquare(landingRow, landingCol - 1)) {
var leftSquare = board[landingRow][landingCol - 1];
if (!leftSquare || !leftSquare.piece || leftSquare.piece.color !== piece.color) {
shredrMoves.push({
row: landingRow,
col: landingCol - 1
});
}
}
// Check horizontal right
if (isValidSquare(landingRow, landingCol + 1)) {
var rightSquare = board[landingRow][landingCol + 1];
if (!rightSquare || !rightSquare.piece || rightSquare.piece.color !== piece.color) {
shredrMoves.push({
row: landingRow,
col: landingCol + 1
});
}
}
}
// Replace original moves with shredr moves (includes original + horizontal)
// Need to clear original moves and add shredrMoves back.
// The 'moves' array is passed by reference, modify it directly.
moves.length = 0; // Clear the array
for (var k = 0; k < shredrMoves.length; k++) {
// Avoid duplicates (simple check, might need improvement for complex scenarios)
var exists = false;
for (var j = 0; j < moves.length; j++) {
if (moves[j].row === shredrMoves[k].row && moves[j].col === shredrMoves[k].col) {
exists = true;
break;
}
}
if (!exists) {
moves.push(shredrMoves[k]);
}
}
}
}
// Calculate valid moves for sliding pieces (rook, bishop, queen)
function calculateSlidingMoves(row, col, piece, moves, directions) {
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
while (isValidSquare(newRow, newCol)) {
var targetSquare = board[newRow][newCol];
if (!targetSquare.piece) {
// Empty square, can move here
moves.push({
row: newRow,
col: newCol
});
} else {
// Found a piece
if (targetSquare.piece.color !== piece.color) {
// Can capture opponent's piece
moves.push({
row: newRow,
col: newCol
});
}
// SHREDR Bishops can jump, so don't break here for them
if (!(game.gameMode === 'specialGame' && game.specialRule === 'shredrBishops' && piece.type === 'bishop')) {
// Can't move past a piece (unless SHREDR bishop)
break;
}
}
newRow += dir.row;
newCol += dir.col;
}
}
}
// Calculate valid moves for a knight
function calculateKnightMoves(row, col, piece, moves) {
var knightDirections = [{
row: -2,
col: -1
}, {
row: -2,
col: 1
}, {
row: -1,
col: -2
}, {
row: -1,
col: 2
}, {
row: 1,
col: -2
}, {
row: 1,
col: 2
}, {
row: 2,
col: -1
}, {
row: 2,
col: 1
}];
for (var i = 0; i < knightDirections.length; i++) {
var dir = knightDirections[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
if (isValidSquare(newRow, newCol)) {
var targetSquare = board[newRow][newCol];
if (!targetSquare.piece || targetSquare.piece.color !== piece.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// Calculate valid moves for a king
function calculateKingMoves(row, col, piece, moves) {
var kingDirections = [{
row: -1,
col: -1
}, {
row: -1,
col: 0
}, {
row: -1,
col: 1
}, {
row: 0,
col: -1
}, {
row: 0,
col: 1
}, {
row: 1,
col: -1
}, {
row: 1,
col: 0
}, {
row: 1,
col: 1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var newRow = row + dir.row;
var newCol = col + dir.col;
if (isValidSquare(newRow, newCol)) {
var targetSquare = board[newRow][newCol];
if (!targetSquare.piece || targetSquare.piece.color !== piece.color) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// Castling
if (!piece.hasMoved) {
// Kingside castling
if (canCastle(row, col, 0, 3)) {
moves.push({
row: row,
col: col + 2
});
}
// Queenside castling
if (canCastle(row, col, 0, -4)) {
moves.push({
row: row,
col: col - 2
});
}
}
}
// Check if castling is possible
function canCastle(row, col, rowOffset, colOffset) {
var rookCol = colOffset > 0 ? 7 : 0;
var rookRow = row;
// Check if there's a rook in the corner
var rookSquare = board[rookRow][rookCol];
if (!rookSquare.piece || rookSquare.piece.type !== 'rook' || rookSquare.piece.hasMoved) {
return false;
}
// Check if there are pieces between the king and the rook
var startCol = Math.min(col, rookCol) + 1;
var endCol = Math.max(col, rookCol) - 1;
for (var c = startCol; c <= endCol; c++) {
if (board[row][c].piece) {
return false;
}
}
// Check if the king would move through or into check
var direction = colOffset > 0 ? 1 : -1;
if (isSquareAttacked(row, col, otherPlayer(currentPlayer)) || isSquareAttacked(row, col + direction, otherPlayer(currentPlayer)) || isSquareAttacked(row, col + 2 * direction, otherPlayer(currentPlayer))) {
return false;
}
return true;
}
// Filter out moves that would put the king in check
function filterCheckMoves(fromSquare, moves) {
var validMoves = [];
var piece = fromSquare.piece;
// Capture the King Rule: King doesn't care about checks, skip filtering
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing') {
// Return all moves unfiltered for check
for (var i = 0; i < moves.length; i++) {
validMoves.push(moves[i]);
}
return validMoves;
}
// Standard check filtering
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
// Make sure the move exists and has valid row/col properties
if (!move || !board[move.row] || !board[move.row][move.col]) {
continue;
}
// Simulate the move
var originalPiece = board[move.row][move.col].piece;
board[move.row][move.col].piece = piece;
board[fromSquare.row][fromSquare.col].piece = null;
// Check if the king is in check after the move
var inCheck = isKingInCheck(piece.color);
// Undo the move
board[fromSquare.row][fromSquare.col].piece = piece;
board[move.row][move.col].piece = originalPiece;
if (!inCheck) {
validMoves.push(move);
}
}
return validMoves;
}
// Check if a square is valid (within the board)
function isValidSquare(row, col) {
// Use dynamic board sizes and column limits
return row >= 0 && row < BOARD_SIZE_Y && col >= BOARD_START_COL && col <= BOARD_END_COL;
}
// Check if the king is in check
function isKingInCheck(color) {
// Find the king
var kingSquare = findKing(color);
if (!kingSquare) {
return false;
}
// Check if the king is attacked by any opponent's piece
return isSquareAttacked(kingSquare.row, kingSquare.col, otherPlayer(color));
}
// Check if a square is attacked by any piece of the given color
function isSquareAttacked(row, col, attackerColor) {
for (var r = 0; r < BOARD_SIZE; r++) {
for (var c = 0; c < BOARD_SIZE; c++) {
var square = board[r][c];
if (square.piece && square.piece.color === attackerColor) {
var moves = [];
switch (square.piece.type) {
case 'pawn':
// Pawns attack diagonally
var direction = square.piece.color === 'white' ? -1 : 1;
var attackDirections = [{
row: direction,
col: -1
}, {
row: direction,
col: 1
}];
for (var i = 0; i < attackDirections.length; i++) {
var dir = attackDirections[i];
var attackRow = r + dir.row;
var attackCol = c + dir.col;
if (attackRow === row && attackCol === col) {
return true;
}
}
break;
case 'rook':
calculateRookMoves(r, c, square.piece, moves);
break;
case 'knight':
calculateKnightMoves(r, c, square.piece, moves);
break;
case 'bishop':
calculateBishopMoves(r, c, square.piece, moves);
break;
case 'queen':
calculateRookMoves(r, c, square.piece, moves);
calculateBishopMoves(r, c, square.piece, moves);
break;
case 'king':
var kingDirections = [{
row: -1,
col: -1
}, {
row: -1,
col: 0
}, {
row: -1,
col: 1
}, {
row: 0,
col: -1
}, {
row: 0,
col: 1
}, {
row: 1,
col: -1
}, {
row: 1,
col: 0
}, {
row: 1,
col: 1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var attackRow = r + dir.row;
var attackCol = c + dir.col;
if (attackRow === row && attackCol === col) {
return true;
}
}
break;
}
// Check if the square is in the list of moves
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
if (move.row === row && move.col === col) {
return true;
}
}
}
}
}
return false;
}
// Find the king of the given color
function findKing(color) {
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.type === 'king' && square.piece.color === color) {
return {
row: row,
col: col
};
}
}
}
return null;
}
// Get the other player's color
function otherPlayer(color) {
return color === 'white' ? 'black' : 'white';
}
// Switch the current player
function switchPlayer() {
currentPlayer = otherPlayer(currentPlayer);
// Icy Day: Update frozen piece counters at the start of the next player's turn
if (game.gameMode === 'specialGame' && game.specialRule === 'icyDay') {
updateIcyDayFrozenPieces();
}
}
// Helper function to update Icy Day frozen pieces
function updateIcyDayFrozenPieces() {
for (var i = icyDayFrozenPieces.length - 1; i >= 0; i--) {
var frozen = icyDayFrozenPieces[i];
frozen.turnsLeft--;
if (frozen.turnsLeft <= 0) {
var square = board[frozen.row][frozen.col];
// Unfreeze if the piece is still there and marked as frozen
if (square && square.piece && square.piece.isFrozenIcy) {
// Reset tint based on whether it's a Yeti or regular piece
if (square.piece.isYeti) {
square.piece.tint = 0xADD8E6; // Yeti tint
} else {
square.piece.tint = 0xFFFFFF; // Default tint
}
square.piece.isFrozenIcy = false; // Unmark
}
icyDayFrozenPieces.splice(i, 1); // Remove from list
}
}
}
// Check the game state (check, checkmate, stalemate)
function checkGameState() {
// Capture the King Rule: Check/Checkmate logic is different
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing') {
// Stalemate can still occur if no legal moves (excluding king capture scenario handled in movePiece)
var allMovesCtK = getAllPossibleMoves(currentPlayer);
if (allMovesCtK.length === 0) {
// Stalemate if no moves and king not captured
gameState = 'stalemate'; //{eT} // Reuse stalemate code line ID if appropriate
endGame('Draw by stalemate!');
return;
}
// Check for 50-move rule - REMOVED
/*
if (movesSinceCaptureOrPawn >= 50) { //{gH} // Keep line ID comment for history
gameState = 'stalemate';
endGame('Draw by 50-move rule!');
return;
}
*/
// Check for insufficient material still applies
if (hasInsufficientMaterial()) {
gameState = 'stalemate';
endGame('Draw by insufficient material!');
return;
}
// No check state in this mode
gameState = 'playing'; //{eV} // Reuse playing state code line ID
return; // Skip standard check/checkmate logic
}
// GOAT-ed Challenge: Check for survival win
if (game.gameMode === 'specialGame' && game.specialRule === 'goatedChallenge') {
if (goatedTurnCounter >= 25) {
gameState = 'checkmate'; // Use checkmate state to signify player win
endGame('Survived 25 turns! You win!');
return;
}
// Check if AI king is captured (AI loses) - should not happen if AI plays normally
var blackKing = findKing('black');
if (!blackKing) {
gameState = 'checkmate';
endGame('AI King lost! You win!');
return;
}
// Check if player king is captured (Player loses)
var whiteKing = findKing('white');
if (!whiteKing) {
gameState = 'checkmate'; // Player lost
endGame('Your King was captured! AI wins!');
return;
}
}
// Dragon Boss: Check if Dragon is captured (Player wins)
if (game.gameMode === 'specialGame' && game.specialRule === 'dragonBoss') {
var dragonFound = false;
for (var r = 0; r < BOARD_SIZE_Y; r++) {
for (var c = BOARD_START_COL; c <= BOARD_END_COL; c++) {
if (board[r][c] && board[r][c].piece && board[r][c].piece.isDragon) {
dragonFound = true;
break;
}
}
if (dragonFound) {
break;
}
}
if (!dragonFound) {
gameState = 'checkmate'; // Player wins
endGame('Dragon Captured! You win!');
return;
}
// Check if player king is captured (Player loses)
var whiteKingDragon = findKing('white');
if (!whiteKingDragon) {
gameState = 'checkmate'; // Player lost
endGame('Your King was captured! Dragon wins!');
return;
}
}
// --- Standard Check/Checkmate/Stalemate Logic ---
// Check if the current player's king is in check
var inCheck = isKingInCheck(currentPlayer);
// Get all possible moves for the current player
var allMoves = getAllPossibleMoves(currentPlayer);
// Check for 50-move rule - REMOVED
/*
if (movesSinceCaptureOrPawn >= 50) { //{hh} // Keep line ID comment for history
gameState = 'stalemate';
endGame('Draw by 50-move rule!');
return;
}
*/
// Check for insufficient material
if (hasInsufficientMaterial()) {
gameState = 'stalemate';
endGame('Draw by insufficient material!');
return;
}
// Check crazy mode king only frozen situation
if (game.gameMode === 'crazy' && isKingOnlyFrozen()) {
gameState = 'stalemate';
endGame('Draw! King is frozen and alone.');
return;
}
if (inCheck) {
if (allMoves.length === 0) {
// Checkmate
gameState = 'checkmate';
endGame('Checkmate! ' + otherPlayer(currentPlayer) + ' wins!');
} else {
// Check
gameState = 'check';
LK.getSound('check').play();
}
} else if (allMoves.length === 0) {
// Stalemate
gameState = 'stalemate';
endGame('Draw by stalemate!');
} else {
gameState = 'playing';
}
}
// Get all possible moves for a player
function getAllPossibleMoves(color) {
var allMoves = [];
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece && square.piece.color === color) {
var moves = calculateValidMoves(square);
for (var i = 0; i < moves.length; i++) {
allMoves.push({
fromRow: row,
fromCol: col,
toRow: moves[i].row,
toCol: moves[i].col
});
}
}
}
}
return allMoves;
}
// Check for insufficient material (K vs K, K vs KB, K vs KN)
function hasInsufficientMaterial() {
var pieces = {
white: {
count: 0,
bishop: 0,
knight: 0
},
black: {
count: 0,
bishop: 0,
knight: 0
}
};
// Count all pieces on the board
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece) {
var piece = square.piece;
pieces[piece.color].count++;
if (piece.type === 'bishop') {
pieces[piece.color].bishop++;
} else if (piece.type === 'knight') {
pieces[piece.color].knight++;
} else if (piece.type !== 'king') {
// If there's any other piece (pawn, rook, queen), material is sufficient
if (piece.type === 'pawn' || piece.type === 'rook' || piece.type === 'queen') {
return false;
}
}
}
}
}
// K vs K
if (pieces.white.count === 1 && pieces.black.count === 1) {
return true;
}
// K vs KB or K vs KN
if (pieces.white.count === 1 && pieces.black.count === 2 && (pieces.black.bishop === 1 || pieces.black.knight === 1) || pieces.black.count === 1 && pieces.white.count === 2 && (pieces.white.bishop === 1 || pieces.white.knight === 1)) {
return true;
}
return false;
}
// Check for king-only frozen situation in crazy mode
function isKingOnlyFrozen() {
if (game.gameMode !== 'crazy') {
return false;
}
var whiteCount = 0;
var whiteKingRow = -1;
var whiteKingCol = -1;
var blackCount = 0;
// Count pieces and find white king
for (var row = 0; row < BOARD_SIZE; row++) {
for (var col = 0; col < BOARD_SIZE; col++) {
var square = board[row][col];
if (square.piece) {
if (square.piece.color === 'white') {
whiteCount++;
if (square.piece.type === 'king') {
whiteKingRow = row;
whiteKingCol = col;
}
} else {
blackCount++;
}
}
}
}
// If white only has the king and it's frozen
return whiteCount === 1 && whiteKingRow !== -1 && crazyAbilities.isPieceFrozen(whiteKingRow, whiteKingCol);
}
// End the game
function endGame(message) {
var gameOverText = new Text2(message, {
size: 60,
fill: 0xFFFFFF
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 2048 / 2;
gameOverText.y = 200;
game.addChild(gameOverText);
// Update score based on winner
if (gameState === 'checkmate') {
if (currentPlayer === 'black') {
// White wins
LK.setScore(100);
} else {
// Black wins (AI wins)
LK.setScore(0);
}
} else {
// Draw
LK.setScore(50);
}
// Show game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
// Make an AI move
function makeAIMove() {
if (gameState === 'checkmate' || gameState === 'stalemate') {
aiThinking = false;
return;
}
// Get all possible moves for the AI
var possibleMoves = getAllPossibleMoves('black');
if (possibleMoves.length === 0) {
aiThinking = false;
return;
}
// Simple AI: pick a random move
var randomMove = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
// Make the move
var fromSquare = board[randomMove.fromRow][randomMove.fromCol];
var toSquare = board[randomMove.toRow][randomMove.toCol];
// Highlight the selected square
fromSquare.highlight('selected');
// Wait a bit before making the move
LK.setTimeout(function () {
// Move the piece
movePiece(fromSquare, toSquare);
// Clear highlights
clearHighlights();
// Check if the move ended the game (e.g., king capture)
// gameState might have been updated in movePiece
if (gameState === 'checkmate' || gameState === 'stalemate') {
aiThinking = false;
return; // Exit if movePiece ended the game
}
// Use crazy ability ONLY in crazy mode
if (game.gameMode === 'crazy' && Math.random() < crazyAbilityChance) {
// Use ability after a short delay
LK.setTimeout(function () {
crazyAbilities.useRandomAbility();
// Note: Ability might change game state, but we check again after switch anyway
}, 500);
}
// Switch back to player
switchPlayer(); //{iS} // currentPlayer is now 'white'
// Check for check/checkmate/stalemate for the player whose turn it now is
checkGameState(); //{iR} // Checks state for 'white'
// If game ended after check
if (gameState === 'checkmate' || gameState === 'stalemate') {
aiThinking = false;
return;
}
// Update crazy mode frozen and goatified pieces ONLY in crazy mode
if (game.gameMode === 'crazy') {
crazyAbilities.updateFrozenPieces();
crazyAbilities.updateGoatifiedPieces();
}
// AI turn is over
aiThinking = false;
}, 500);
}
// Create score display
var scoreTxt = new Text2('Crazy Chess', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create turn display
var turnText = new Text2('Your Turn', {
size: 40,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.y = 70;
LK.gui.top.addChild(turnText);
// Game update function
game.update = function () {
// If in title screen, update title screen
if (gameState === 'title' && titleScreen) {
titleScreen.update();
return;
}
// If in game mode selection screen, update that screen
if (gameState === 'modeSelect' && gameModeScreen) {
gameModeScreen.update();
return;
}
// If in bot selection screen, update that screen
if (gameState === 'botSelect' && botSelectionScreen) {
botSelectionScreen.update();
return;
}
// If in daily challenge screen, update that screen
if (gameState === 'dailyChallenge' && dailyChallengeScreen) {
dailyChallengeScreen.update();
return; // Stop further updates if in this screen
}
// Fog of War visual update (placeholder)
if (game.gameMode === 'specialGame' && game.specialRule === 'fogOfWar') {
// updateFogOfWar(); // TODO: Implement visual fog logic
}
// Update UI Text for Special Modes
if (game.gameMode === 'specialGame') {
if (game.specialRule === 'goatedChallenge') {
scoreTxt.setText('GOAT-ed Challenge');
turnText.setText('Turn: ' + goatedTurnCounter + '/25');
if (gameState === 'checkmate' && goatedTurnCounter >= 25) {
turnText.setText('You Win!');
} else if (gameState === 'checkmate') {
turnText.setText('AI Wins!'); // Player lost before 25 turns
} else if (aiThinking) {
turnText.setText('AI Thinking...');
}
// Don't overwrite win/loss text immediately
return; // Skip default turn text logic for this mode if not thinking
} else if (game.specialRule === 'dragonBoss') {
scoreTxt.setText('Dragon Boss');
if (gameState === 'checkmate') {
var whiteKingDragon = findKing('white');
turnText.setText(whiteKingDragon ? 'You Win!' : 'Dragon Wins!');
} else if (aiThinking) {
turnText.setText('Dragon Thinking...');
} else {
turnText.setText('Your Turn');
}
return; // Skip default turn text logic
} else if (game.specialRule === 'icyDay') {
scoreTxt.setText('Icy Day');
// Use default turn text logic below
} else if (game.specialRule === 'transformoFest') {
scoreTxt.setText('Transformo-fest');
// Use default turn text logic below
}
// Add other special rule title updates here if needed
}
// Update turn text (Default logic, handles standard game and some special modes)
if (gameState === 'checkmate') {
// Capture the King uses 'checkmate' state for win, adjust text
if (game.gameMode === 'specialGame' && game.specialRule === 'captureKing') {
// Winner determined in endGame, turnText might be showing winner already
} else if (currentPlayer === 'black') {
turnText.setText('You Win!');
} else {
turnText.setText('AI Wins!');
}
} else if (gameState === 'stalemate') {
turnText.setText('Draw!');
} else if (aiThinking) {
turnText.setText('AI Thinking...');
} else {
turnText.setText(currentPlayer === 'white' ? 'Your Turn' : 'AI Turn');
}
};
// Method to show the game mode selection screen
game.showGameModeScreen = function () {
// Remove title screen if it exists
if (titleScreen) {
game.removeChild(titleScreen);
titleScreen = null;
}
// Remove game mode screen if it already exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null; // Clear reference
}
// Remove bot selection screen if it exists
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Remove daily challenge screen if it exists
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Create and add game mode screen
gameModeScreen = new GameModeScreen();
gameModeScreen.x = 2048 / 2;
gameModeScreen.y = 2732 / 2;
game.addChild(gameModeScreen);
// Update game state
gameState = 'modeSelect';
};
// Method to show the title screen
// Method to show the bot selection screen
game.showBotSelectionScreen = function () {
// Remove game mode screen if it exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
// Remove title screen just in case (shouldn't happen in normal flow)
if (titleScreen) {
game.removeChild(titleScreen);
titleScreen = null;
}
// Remove existing bot selection screen if present
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Create and add bot selection screen
botSelectionScreen = new BotSelectionScreen();
botSelectionScreen.x = 2048 / 2;
botSelectionScreen.y = 2732 / 2;
game.addChild(botSelectionScreen);
// Update game state
gameState = 'botSelect';
};
// Method to show the daily challenge screen
game.showDailyChallengeScreen = function () {
// Remove other screens
if (titleScreen) {
game.removeChild(titleScreen);
titleScreen = null;
}
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Create and add daily challenge screen
dailyChallengeScreen = new DailyChallengeScreen();
dailyChallengeScreen.x = 2048 / 2;
dailyChallengeScreen.y = 2732 / 2;
game.addChild(dailyChallengeScreen);
// Update game state
gameState = 'dailyChallenge';
};
game.showTitleScreen = function () {
// Remove game mode screen if it exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
// Remove bot selection screen if it exists
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Remove daily challenge screen if it exists
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Create and add title screen
titleScreen = new TitleScreen();
titleScreen.x = 2048 / 2;
titleScreen.y = 2732 / 2;
game.addChild(titleScreen);
// Update game state
gameState = 'title';
};
// Method to start the game from mode selection screen
game.startGame = function () {
// Remove game mode screen if it exists
if (gameModeScreen) {
game.removeChild(gameModeScreen);
gameModeScreen = null;
}
// Remove bot selection screen if it exists
if (botSelectionScreen) {
game.removeChild(botSelectionScreen);
botSelectionScreen = null;
}
// Remove daily challenge screen if it exists (shouldn't be possible to start game from here, but for safety)
if (dailyChallengeScreen) {
game.removeChild(dailyChallengeScreen);
dailyChallengeScreen = null;
}
// Change state based on mode
if (game.gameMode === 'specialGame') {
gameState = 'specialGame'; // Or keep as 'playing' and rely on game.specialRule
scoreTxt.setText('Daily Challenge - ' + selectedBot.toUpperCase()); // Or show special rule name?
} else {
gameState = 'playing';
// Update game title based on selected mode
scoreTxt.setText('Crazy Chess - ' + game.gameMode.toUpperCase());
}
// Initialize the chess board
initializeBoard();
// Reset special mode variables
goatedTurnCounter = 0;
icyDayFrozenPieces = [];
yetiPawns = [];
// Show the score and turn text
scoreTxt.visible = true;
turnText.visible = true;
};
// Show title screen first
titleScreen = new TitleScreen();
titleScreen.x = 2048 / 2;
titleScreen.y = 2732 / 2;
game.addChild(titleScreen);
// Hide score and turn text initially
scoreTxt.visible = false;
turnText.visible = false;
;
Black horse. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White horse. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Black lawnmower. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White lawnmower. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Black crown. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White crown. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White cannon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Black cannon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Black queen. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White queen. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Black pawn. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White pawn. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White goat. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Bear trap. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows