User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of undefined (reading 'piece')' in or related to this line: 'var originalPiece = board[move.row][move.col].piece;' Line Number: 1869
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'highlight')' in or related to this line: 'board[row][col].highlight();' Line Number: 1472
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'highlight')' in or related to this line: 'board[row][col].highlight();' Line Number: 1472
User prompt
The game AI freezes when it's in check and has no available moves; make it so that it IS checkmate
User prompt
The game AI freezes when it's in check and has no available moves; make it so that it IS checkmate and fix it so that there's a message " YOUR OPPONENT JUST GOT CHECKMATED! LAUGH AT THEM NOW IF YOU WANNA KEEP PLAYING IN THE CARNIVAL!"
User prompt
Remove the 50 move rule
User prompt
Add new special gamemodes: Transformo-fest (all pieces [except kings] turn into random pieces after every move [but not into kings]); Dragon boss (a dragon boss with the same moves as a queen and a knight combines challenges your king! Try to capture the dragon only using 3 knights and 4 bishops on the board to win!); GOAT-ed challenge (all your pieces [except the king] are permanently Goatified! Try to survive 25 turns to win!); Icy day (Each piece on the board [except kings] has a chance to be frozen after a move; but don't fear! Some pawns turned into Yetis that makes them immune to this madness!)
User prompt
Now daily challenges only Special games instead of chess puzzles and quizzes
User prompt
Please fix the bug: 'Uncaught ReferenceError: BOARD_SIZE is not defined' in or related to this line: 'for (var row = 0; row < BOARD_SIZE; row++) {' Line Number: 1364
/**** * 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); // Challenge data (placeholders for now) var challengeTypes = [{ type: 'puzzle', title: 'Chess Puzzle', description: 'Solve the puzzle to win!\n(e.g., Checkmate in 1)' }, { type: 'questions', title: 'Chess Trivia', description: 'Answer 5 questions about chess history and rules.' }, { type: 'specialGame', title: 'Special Rules Game', description: 'Play a game against a bot with unique rules!', rules: [{ 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' }] }]; // Select a random challenge for the "day" var dailyChallenge = challengeTypes[Math.floor(Math.random() * challengeTypes.length)]; var selectedSpecialRule = null; // --- UI Elements --- // Title Text var titleText = new Text2('Daily Challenge: ' + dailyChallenge.title, { 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); // Determine description based on challenge type var finalDescription = dailyChallenge.description; if (dailyChallenge.type === 'specialGame') { // Select a random special rule selectedSpecialRule = dailyChallenge.rules[Math.floor(Math.random() * dailyChallenge.rules.length)]; finalDescription = 'Special Rule: ' + selectedSpecialRule.name + '\n' + selectedSpecialRule.description; titleText.setText('Daily Challenge: Special Game'); // Update title for special game } // 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 }); } }); // Placeholder: Log the selected challenge type and go back console.log("Starting Daily Challenge:", dailyChallenge.type); if (dailyChallenge.type === 'specialGame') { // 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 } else { // TODO: Implement actual challenge start logic for 'puzzle' and 'questions' // For now, show a temporary message and go back var startMsg = new Text2("Challenge Type: " + dailyChallenge.title + "\n(Implementation Pending for this type)", { size: 50, fill: 0xFFFF00, align: 'center' }); startMsg.anchor.set(0.5, 0.5); startMsg.x = 2048 / 2; startMsg.y = 2732 / 2; game.addChild(startMsg); // Remove this screen if (game.dailyChallengeScreen) { game.removeChild(game.dailyChallengeScreen); game.dailyChallengeScreen = null; } // Go back after a delay LK.setTimeout(function () { game.removeChild(startMsg); game.showGameModeScreen(); // Go back to mode select }, 2500); } }; })(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 // 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 { // 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')); } // Special rule for 'King' bot: remove the black queen if (game.selectedBot === 'King') { 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; } // 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 movePiece(selectedSquare, square); // Reset selection selectedSquare = null; validMoves = []; // Check for check/checkmate checkGameState(); // If game is still playing, switch to AI turn if (gameState === 'playing' || gameState === 'check') { switchPlayer(); // Start AI turn 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 }; } // 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++) { 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 if piece is goatified (Only in crazy mode) 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]; // 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); } // 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 and insufficient material still applies if (movesSinceCaptureOrPawn >= 50) { gameState = 'stalemate'; endGame('Draw by 50-move rule!'); return; } 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 } // --- 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 if (movesSinceCaptureOrPawn >= 50) { 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(); // Use crazy ability only in crazy mode (not special game mode) if (game.gameMode === 'crazy' && Math.random() < crazyAbilityChance) { // Use ability after a short delay LK.setTimeout(function () { crazyAbilities.useRandomAbility(); }, 500); } // Check for check/checkmate checkGameState(); // Switch back to player switchPlayer(); // Update 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 turn text 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(); // 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);
// Challenge data (placeholders for now)
var challengeTypes = [{
type: 'puzzle',
title: 'Chess Puzzle',
description: 'Solve the puzzle to win!\n(e.g., Checkmate in 1)'
}, {
type: 'questions',
title: 'Chess Trivia',
description: 'Answer 5 questions about chess history and rules.'
}, {
type: 'specialGame',
title: 'Special Rules Game',
description: 'Play a game against a bot with unique rules!',
rules: [{
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'
}]
}];
// Select a random challenge for the "day"
var dailyChallenge = challengeTypes[Math.floor(Math.random() * challengeTypes.length)];
var selectedSpecialRule = null;
// --- UI Elements ---
// Title Text
var titleText = new Text2('Daily Challenge: ' + dailyChallenge.title, {
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);
// Determine description based on challenge type
var finalDescription = dailyChallenge.description;
if (dailyChallenge.type === 'specialGame') {
// Select a random special rule
selectedSpecialRule = dailyChallenge.rules[Math.floor(Math.random() * dailyChallenge.rules.length)];
finalDescription = 'Special Rule: ' + selectedSpecialRule.name + '\n' + selectedSpecialRule.description;
titleText.setText('Daily Challenge: Special Game'); // Update title for special game
}
// 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
});
}
});
// Placeholder: Log the selected challenge type and go back
console.log("Starting Daily Challenge:", dailyChallenge.type);
if (dailyChallenge.type === 'specialGame') {
// 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
} else {
// TODO: Implement actual challenge start logic for 'puzzle' and 'questions'
// For now, show a temporary message and go back
var startMsg = new Text2("Challenge Type: " + dailyChallenge.title + "\n(Implementation Pending for this type)", {
size: 50,
fill: 0xFFFF00,
align: 'center'
});
startMsg.anchor.set(0.5, 0.5);
startMsg.x = 2048 / 2;
startMsg.y = 2732 / 2;
game.addChild(startMsg);
// Remove this screen
if (game.dailyChallengeScreen) {
game.removeChild(game.dailyChallengeScreen);
game.dailyChallengeScreen = null;
}
// Go back after a delay
LK.setTimeout(function () {
game.removeChild(startMsg);
game.showGameModeScreen(); // Go back to mode select
}, 2500);
}
};
})(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
// 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 {
// 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'));
}
// Special rule for 'King' bot: remove the black queen
if (game.selectedBot === 'King') {
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;
}
// 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
movePiece(selectedSquare, square);
// Reset selection
selectedSquare = null;
validMoves = [];
// Check for check/checkmate
checkGameState();
// If game is still playing, switch to AI turn
if (gameState === 'playing' || gameState === 'check') {
switchPlayer();
// Start AI turn
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
};
}
// 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++) {
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 if piece is goatified (Only in crazy mode)
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];
// 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);
}
// 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 and insufficient material still applies
if (movesSinceCaptureOrPawn >= 50) {
gameState = 'stalemate';
endGame('Draw by 50-move rule!');
return;
}
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
}
// --- 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
if (movesSinceCaptureOrPawn >= 50) {
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();
// Use crazy ability only in crazy mode (not special game mode)
if (game.gameMode === 'crazy' && Math.random() < crazyAbilityChance) {
// Use ability after a short delay
LK.setTimeout(function () {
crazyAbilities.useRandomAbility();
}, 500);
}
// Check for check/checkmate
checkGameState();
// Switch back to player
switchPlayer();
// Update 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 turn text
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();
// 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