User prompt
The opponent AI sometimes makes wrong moves or waits for 2 seconds to think before making a move or imitates the player's move. In these 3 cases, there is a 19% chance.
User prompt
The text does not come out of the box, it always goes down one line within the box
User prompt
Please fix the bug: 'Timeout.tick error: can't access property "undefined", col is undefined' in or related to this line: 'return gameBoard[row][col];' Line Number: 905
User prompt
If the white team's time runs out, the black team wins message appears.
User prompt
If the player's cursor is held over any chess piece for 2 seconds without clicking, the feature of that piece will appear as text in a box. When the player's cursor disappears from that piece, the box and text will disappear.
User prompt
Process the snake stone's stamina consumption in its third move, not its first, so the stamina decreases after the snake stone finishes its move.
User prompt
The stamina rule is broken with the snake stone, as the snake stone's 3 moves require 1 stamina, and the stamina square is tied to the stone. If the stone is captured and there is no stamina, the pink square is destroyed along with the stone.
User prompt
Snake Stone's 3 moves consume 1 stamina, but if it poisons, it also consumes 1 stamina.
User prompt
Ai stamina has the same rules and mechanics as us, and it says "enemy stamina" instead of Ai stamina.
User prompt
Since the snake's 3 moves are counted as 1 move, the snake takes only 1 stamina when played 3 times, the snake cannot poison the king, the jester cannot capture the king and add a special sound asset for the jester to capture it with the checker move.
User prompt
Let's see the stamina of your opponent
User prompt
Only the square under the queen and snake will be pink. Nothing else will be pink. Fix this error.
User prompt
The squares containing the queen and snake pieces turn pink to indicate they cannot be played when stamina is depleted. This pink square disappears when stamina is refilled and returns when stamina is depleted. Capturing a snake and queen piece does not add to the stamina bar. The opponent's AI also has a stamina bar and plays the queen and snake pieces according to the stamina rules. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add three stamina bars to the game. Playing the Queen and Snake pieces costs one point from this bar, and each capture adds one point to this bar. Once three points are filled, no more points are added until a point is spent. When there are no more points, the square containing the right-hand pieces turns pink. This bar is also pink. When the game starts, this bar only contains one point. Playing the Queen and Snake pieces requires one point, and a piece must be captured to earn points.
User prompt
Delete all updates and innovations related to the queen piece and return the game to the state before the new queen arrived, without changing the queen.
User prompt
Examine all pieces and restore their movement patterns. Make all white pieces playable. And fix any related bugs.
User prompt
The pieces are not moving, I can't start the game, fix this bug
User prompt
add assets for new queen
User prompt
Remove the queen piece completely and add a new quenn in its place. quenn can teleport right behind any piece it wants and can go anywhere it wants in a 4x4 square area.
User prompt
Put the timer above the "a" text on the black side and below the "a" text on the white side
User prompt
This timer appears on each side of the screen.
User prompt
Please fix the bug: 'can't access property "setText", whiteTimerText is undefined' in or related to this line: 'whiteTimerText.setText(formatTime(whiteTimeRemaining));' Line Number: 2527
User prompt
Both sides have a total of 1 minute, and this timer decreases when it is their turn to move and stops when it passes to the other side. In other words, the classic chess timer.
User prompt
The jester piece can jump over its own side's pawns ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The summoner chess piece can only move 1 square in any direction with each move.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ChessPiece = Container.expand(function (type, color, row, col) {
var self = Container.call(this);
self.pieceType = type;
self.pieceColor = color;
self.row = row;
self.col = col;
self.hasMoved = false;
self.poisonTurns = 0;
self.poisonEffect = null;
self.poisonCounterText = null;
self.snakeMovesThisTurn = 0;
self.maxSnakeMoves = 3;
self.snakeLastMoveDirection = null; // Track last move direction for straight ahead requirement
self.snakeOnCooldown = false; // Prevent consecutive snake usage
self.princessEliminationMode = false; // Track if princess is in elimination mode
self.princessTargetsSelected = 0; // Track how many targets princess has selected
self.princessSelectedTargets = []; // Store selected targets for elimination
self.summonerMoveCount = 0; // Track moves for summoner ability
self.hasSummoned = false; // Track if summoner has summoned this cycle
var assetName = color + type.charAt(0).toUpperCase() + type.slice(1);
var pieceGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.updatePosition = function (animate) {
var targetX = boardStartX + self.col * squareSize + squareSize / 2;
var targetY = boardStartY + self.row * squareSize + squareSize / 2;
if (animate && (self.x !== targetX || self.y !== targetY)) {
// Add subtle bounce animation for piece movement
tween(self, {
x: targetX,
y: targetY,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Bounce back to normal size
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.bounceOut
});
}
});
} else {
self.x = targetX;
self.y = targetY;
}
};
self.canMoveTo = function (targetRow, targetCol) {
// Poisoned pieces cannot move
if (self.poisonTurns > 0) {
return false;
}
if (targetRow < 0 || targetRow > 7 || targetCol < 0 || targetCol > 15) {
return false;
}
var targetPiece = getPieceAt(targetRow, targetCol);
if (targetPiece && targetPiece.pieceColor === self.pieceColor) {
return false;
}
switch (self.pieceType) {
case 'pawn':
return self.canPawnMove(targetRow, targetCol);
case 'rook':
return self.canRookMove(targetRow, targetCol);
case 'knight':
return self.canKnightMove(targetRow, targetCol);
case 'bishop':
return self.canBishopMove(targetRow, targetCol);
case 'queen':
// Check stamina for queen moves
if (self.pieceColor === 'white' && staminaPoints <= 0) {
return false;
}
if (self.pieceColor === 'black' && aiStaminaPoints <= 0) {
return false;
}
return self.canQueenMove(targetRow, targetCol);
case 'princess':
return self.canPrincessMove(targetRow, targetCol);
case 'king':
return self.canKingMove(targetRow, targetCol);
case 'snake':
// Check stamina for snake moves
if (self.pieceColor === 'white' && staminaPoints <= 0) {
return false;
}
if (self.pieceColor === 'black' && aiStaminaPoints <= 0) {
return false;
}
return self.canSnakeMove(targetRow, targetCol);
case 'jester':
return self.canJesterMove(targetRow, targetCol);
case 'summoner':
return self.canSummonerMove(targetRow, targetCol);
}
return false;
};
self.canPawnMove = function (targetRow, targetCol) {
var direction = self.pieceColor === 'white' ? -1 : 1;
var startRow = self.pieceColor === 'white' ? 6 : 1;
if (targetCol === self.col) {
if (targetRow === self.row + direction && !getPieceAt(targetRow, targetCol)) {
return true;
}
if (self.row === startRow && targetRow === self.row + 2 * direction && !getPieceAt(targetRow, targetCol)) {
// Check if path is clear for 2-square pawn move
return isPathClear(self.row, self.col, targetRow, targetCol);
}
} else if (Math.abs(targetCol - self.col) === 1 && targetRow === self.row + direction) {
var targetPiece = getPieceAt(targetRow, targetCol);
if (targetPiece && targetPiece.pieceColor !== self.pieceColor) {
return true;
}
}
return false;
};
self.canRookMove = function (targetRow, targetCol) {
if (self.row !== targetRow && self.col !== targetCol) {
return false;
}
return isPathClear(self.row, self.col, targetRow, targetCol);
};
self.canKnightMove = function (targetRow, targetCol) {
var rowDiff = Math.abs(targetRow - self.row);
var colDiff = Math.abs(targetCol - self.col);
return rowDiff === 2 && colDiff === 1 || rowDiff === 1 && colDiff === 2;
};
self.canBishopMove = function (targetRow, targetCol) {
if (Math.abs(targetRow - self.row) !== Math.abs(targetCol - self.col)) {
return false;
}
return isPathClear(self.row, self.col, targetRow, targetCol);
};
self.canPrincessMove = function (targetRow, targetCol) {
var rowDiff = Math.abs(targetRow - self.row);
var colDiff = Math.abs(targetCol - self.col);
// Princess can move exactly 2 squares straight (horizontal or vertical only)
if (rowDiff === 2 && colDiff === 0 || rowDiff === 0 && colDiff === 2) {
// Cannot jump over pieces - path must be clear
return isPathClear(self.row, self.col, targetRow, targetCol);
}
// Princess can move 1 square straight for positioning
if (rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1) {
return true;
}
return false;
};
self.canQueenMove = function (targetRow, targetCol) {
// Queen moves like both rook and bishop (any distance in straight lines or diagonals)
if (self.row === targetRow || self.col === targetCol) {
// Rook-like movement (horizontal or vertical)
return isPathClear(self.row, self.col, targetRow, targetCol);
}
if (Math.abs(targetRow - self.row) === Math.abs(targetCol - self.col)) {
// Bishop-like movement (diagonal)
return isPathClear(self.row, self.col, targetRow, targetCol);
}
return false;
};
self.canKingMove = function (targetRow, targetCol) {
var rowDiff = Math.abs(targetRow - self.row);
var colDiff = Math.abs(targetCol - self.col);
// Normal king move (one square in any direction)
if (rowDiff <= 1 && colDiff <= 1) {
// Check if king would be safe at target position
return isKingSafeAt(self, targetRow, targetCol);
}
// Castling logic (king moves 2 squares horizontally)
if (!self.hasMoved && rowDiff === 0 && colDiff === 2) {
// Check if castling is possible
if (targetCol > self.col) {
// King-side castling (right)
var rook = getPieceAt(self.row, 15); // Rightmost rook
if (rook && rook.pieceType === 'rook' && !rook.hasMoved && rook.pieceColor === self.pieceColor) {
// Check if path is clear and king doesn't move through check
for (var col = self.col + 1; col < 15; col++) {
if (getPieceAt(self.row, col) || !isKingSafeAt(self, self.row, col)) {
return false;
}
}
return isKingSafeAt(self, targetRow, targetCol);
}
} else {
// Queen-side castling (left)
var rook = getPieceAt(self.row, 0); // Leftmost rook
if (rook && rook.pieceType === 'rook' && !rook.hasMoved && rook.pieceColor === self.pieceColor) {
// Check if path is clear and king doesn't move through check
for (var col = self.col - 1; col > 0; col--) {
if (getPieceAt(self.row, col) || !isKingSafeAt(self, self.row, col)) {
return false;
}
}
return isKingSafeAt(self, targetRow, targetCol);
}
}
}
return false;
};
self.applyPoison = function (turns) {
self.poisonTurns = turns;
self.showPoisonEffect();
};
self.showPoisonEffect = function () {
if (self.poisonEffect) {
self.poisonEffect.destroy();
self.poisonEffect = null;
}
if (self.poisonCounterText) {
self.poisonCounterText.destroy();
self.poisonCounterText = null;
}
if (self.poisonTurns > 0) {
self.poisonEffect = self.attachAsset('poisonEffect', {
anchorX: 0.5,
anchorY: 0.5
});
self.poisonEffect.x = 30;
self.poisonEffect.y = -30;
self.poisonCounterText = new Text2(self.poisonTurns.toString(), {
size: 24,
fill: 0xFFFFFF
});
self.poisonCounterText.anchor.set(0.5, 0.5);
self.poisonCounterText.x = 30;
self.poisonCounterText.y = -30;
self.addChild(self.poisonCounterText);
// Tint piece red when poisoned
if (pieceGraphics && !pieceGraphics.destroyed) {
tween(pieceGraphics, {
tint: 0xff6666
}, {
duration: 300,
easing: tween.easeOut
});
}
} else {
// Remove red tint when poison wears off
if (pieceGraphics && !pieceGraphics.destroyed) {
tween(pieceGraphics, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
}
}
};
self.reducePoisonTurns = function () {
if (self.poisonTurns > 0) {
self.poisonTurns--;
self.showPoisonEffect();
}
};
self.canJesterMove = function (targetRow, targetCol) {
// Jester moves exactly 2 squares in any direction (like checker piece)
var rowDiff = targetRow - self.row;
var colDiff = targetCol - self.col;
var absRowDiff = Math.abs(rowDiff);
var absColDiff = Math.abs(colDiff);
// Must move exactly 2 squares in any direction
if (Math.max(absRowDiff, absColDiff) !== 2) {
return false;
}
// Check if move is in valid direction (straight or diagonal, 2 squares)
var isValidDirection = false;
if (absRowDiff === 2 && absColDiff === 0 ||
// Vertical 2 squares
absRowDiff === 0 && absColDiff === 2 ||
// Horizontal 2 squares
absRowDiff === 2 && absColDiff === 2) {
// Diagonal 2 squares
isValidDirection = true;
}
if (!isValidDirection) {
return false;
}
// Calculate middle square position
var middleRow = self.row + (rowDiff > 0 ? 1 : rowDiff < 0 ? -1 : 0);
var middleCol = self.col + (colDiff > 0 ? 1 : colDiff < 0 ? -1 : 0);
// Check if middle square is within bounds
if (middleRow < 0 || middleRow > 7 || middleCol < 0 || middleCol > 15) {
return false;
}
var middlePiece = getPieceAt(middleRow, middleCol);
var targetPiece = getPieceAt(targetRow, targetCol);
// Jester cannot land on an occupied square - target square must always be empty
// Also jester cannot capture the king
if (targetPiece) {
if (targetPiece.pieceType === 'king') {
return false; // Jester cannot capture the king
}
return false; // Cannot capture pieces on destination square
}
// Normal move: middle square empty, target square empty
if (!middlePiece) {
return true; // Can move to empty square through empty middle
}
// Jump capture move: middle square has enemy piece, target square must be empty (already checked above)
if (middlePiece.pieceColor !== self.pieceColor) {
return true; // Can jump over enemy piece to empty square
}
// Can jump over own pawns
if (middlePiece.pieceColor === self.pieceColor && middlePiece.pieceType === 'pawn') {
return true; // Can jump over own pawns to empty square
}
return false; // Can't jump over other own pieces
};
self.canSummonerMove = function (targetRow, targetCol) {
// Summoner can only move 1 square in any direction (like a king)
var rowDiff = Math.abs(targetRow - self.row);
var colDiff = Math.abs(targetCol - self.col);
// Summoner can move exactly 1 square in any direction (horizontal, vertical, or diagonal)
if (rowDiff <= 1 && colDiff <= 1 && (rowDiff > 0 || colDiff > 0)) {
return true;
}
return false;
};
self.canSnakeMove = function (targetRow, targetCol) {
// Check if snake is on cooldown
if (self.snakeOnCooldown) {
return false;
}
var rowDiff = targetRow - self.row;
var colDiff = targetCol - self.col;
var distance = Math.max(Math.abs(rowDiff), Math.abs(colDiff));
// Check if poisoning will occur
var willPoison = false;
var poisonPositions = [{
row: self.row - 1,
col: self.col - 1
}, {
row: self.row - 1,
col: self.col
}, {
row: self.row - 1,
col: self.col + 1
}, {
row: self.row,
col: self.col - 1
}, {
row: self.row,
col: self.col + 1
}, {
row: self.row + 1,
col: self.col - 1
}, {
row: self.row + 1,
col: self.col
}, {
row: self.row + 1,
col: self.col + 1
}];
for (var i = 0; i < poisonPositions.length; i++) {
var pos = poisonPositions[i];
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
var pieceAtPos = getPieceAt(pos.row, pos.col);
if (pieceAtPos && pieceAtPos.pieceColor !== self.pieceColor) {
willPoison = true;
break;
}
}
}
// Snake can only move exactly 1 square
if (distance !== 1) {
return false;
}
// Check if the path is clear (no jumping over pieces)
if (!isPathClear(self.row, self.col, targetRow, targetCol)) {
return false;
}
// First move: 1 square in any direction (straight lines only)
if (self.snakeMovesThisTurn === 0) {
// Must be straight line movement (horizontal, vertical, or diagonal)
if (rowDiff === 0 || colDiff === 0 || Math.abs(rowDiff) === Math.abs(colDiff)) {
return true;
}
return false;
}
// Second move: 1 square diagonally in any direction
if (self.snakeMovesThisTurn === 1) {
// Must be diagonal movement
if (Math.abs(rowDiff) === Math.abs(colDiff) && Math.abs(rowDiff) === 1) {
return true;
}
return false;
}
// Third move: 1 square in any direction - only if not poisoning
if (self.snakeMovesThisTurn === 2 && !willPoison) {
// Can move to any adjacent square (any direction)
return true;
}
return false;
};
self.executeSummon = function () {
// Check if summoner can summon (moved 3 times and not already summoned)
if (self.summonerMoveCount < 3 || self.hasSummoned) return;
var summonPositions = [
// Orthogonal squares first: up, right, down, left
{
row: self.row - 1,
col: self.col
},
// up
{
row: self.row,
col: self.col + 1
},
// right
{
row: self.row + 1,
col: self.col
},
// down
{
row: self.row,
col: self.col - 1
},
// left
// Diagonal squares: up-right, down-right, down-left, up-left
{
row: self.row - 1,
col: self.col + 1
},
// up-right
{
row: self.row + 1,
col: self.col + 1
},
// down-right
{
row: self.row + 1,
col: self.col - 1
},
// down-left
{
row: self.row - 1,
col: self.col - 1
} // up-left
];
var summonedCount = 0;
var maxSummons = 2;
for (var i = 0; i < summonPositions.length && summonedCount < maxSummons; i++) {
var pos = summonPositions[i];
// Check if position is valid and empty
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
if (!getPieceAt(pos.row, pos.col)) {
// Summon pawn at this position
var newPawn = new ChessPiece('pawn', self.pieceColor, pos.row, pos.col);
pieces.push(newPawn);
gameBoard[pos.row][pos.col] = newPawn;
game.addChild(newPawn);
newPawn.updatePosition(true);
summonedCount++;
// Create summoning effect
tween(newPawn, {
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.5
}, {
duration: 0
});
tween(newPawn, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 600,
easing: tween.bounceOut
});
}
}
}
if (summonedCount > 0) {
// Reset summoner state
self.summonerMoveCount = 0;
self.hasSummoned = true;
// Show summoning message
var summonText = new Text2('Summoner conjured ' + summonedCount + ' pawn' + (summonedCount > 1 ? 's' : '') + '!', {
size: 50,
fill: self.pieceColor === 'white' ? 0xFFFFFF : 0xFF0000
});
summonText.anchor.set(0.5, 0.5);
summonText.x = 1024;
summonText.y = 1200;
summonText.alpha = 0;
game.addChild(summonText);
tween(summonText, {
alpha: 1.0,
y: summonText.y - 50
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(summonText, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
summonText.destroy();
}
});
}, 1500);
}
});
}
};
// Add hover effects for better interactivity
self.startHover = function () {
tween(self, {
scaleX: 1.05,
scaleY: 1.05,
y: self.y - 5
}, {
duration: 200,
easing: tween.easeOut
});
};
self.endHover = function () {
tween(self, {
scaleX: 1.0,
scaleY: 1.0,
y: boardStartY + self.row * squareSize + squareSize / 2
}, {
duration: 200,
easing: tween.easeOut
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2D1810
});
/****
* Game Code
****/
// Game dimensions and board setup
// Chess board squares
// White pieces
// Black pieces
// Sound effects
var squareSize = 128;
var boardStartX = (2048 - squareSize * 16) / 2;
var boardStartY = (2732 - squareSize * 8) / 2 + 100;
// Game state
var currentPlayer = 'white';
var selectedPiece = null;
var validMoves = [];
var pieces = [];
var gameBoard = [];
var highlightSquares = [];
var validMoveSquares = [];
var escapeSquares = [];
var kingSquare = null;
var blackKingSquare = null;
var dragNode = null;
var gameState = 'playing'; // playing, check, checkmate, stalemate
var whiteKingLives = 4;
var blackKingLives = 4;
var currentSnakePiece = null;
var snakeMovesRemaining = 0;
var activeSnakePiece = null; // Track which specific snake is currently active
// Stamina system variables
var staminaPoints = 1; // Start with 1 point
var maxStaminaPoints = 3;
var staminaContainer = null;
var staminaBars = [];
var aiStaminaBars = [];
// Pink square overlays for Queen and Snake pieces when stamina is depleted
var queenSnakePinkSquares = [];
// AI stamina system variables
var aiStaminaPoints = 1; // AI starts with 1 point
var aiMaxStaminaPoints = 3;
// Timer variables
var whiteTimeRemaining = 60000; // 60 seconds in milliseconds
var blackTimeRemaining = 60000; // 60 seconds in milliseconds
var timerInterval = null;
var gameStartTime = null;
var currentTurnStartTime = null;
// Initialize empty board
var _loop = function _loop() {
gameBoard[row] = [];
for (col = 0; col < 16; col++) {
gameBoard[row][col] = null;
}
// Missing function definitions for AI evaluation
function calculatePerfectActivity(move) {
// Simple activity calculation based on piece mobility
return simulateMove(move, function () {
var mobility = 0;
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (move.piece.canMoveTo(row, col)) {
mobility++;
}
}
}
return mobility;
});
}
function evaluatePerfectPawnStructure(move) {
// Simple pawn structure evaluation
var score = 0;
if (move.piece.pieceType === 'pawn') {
// Bonus for pawn advancement
var advancement = move.piece.pieceColor === 'black' ? move.toRow - move.piece.row : move.piece.row - move.toRow;
if (advancement > 0) score += advancement * 2;
}
return score;
}
function evaluateEndgameTechnique(move) {
// Simple endgame evaluation
var totalPieces = pieces.length;
if (totalPieces < 12) {
// In endgame, centralize king and activate pieces
var score = 0;
if (move.piece.pieceType === 'king') {
var centerDistance = Math.abs(move.toRow - 3.5) + Math.abs(move.toCol - 7.5);
score += (16 - centerDistance) * 3;
}
return score;
}
return 0;
}
function evaluateGrandmasterPositional(move) {
// Simple positional evaluation
return evaluatePositionalFactors(move) + evaluatePieceCoordination(move);
}
},
col;
for (var row = 0; row < 8; row++) {
_loop();
}
// Create visual board
var boardContainer = game.addChild(new Container());
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
var isLight = (row + col) % 2 === 0;
var square = LK.getAsset(isLight ? 'lightSquare' : 'darkSquare', {
anchorX: 0,
anchorY: 0
});
square.x = boardStartX + col * squareSize;
square.y = boardStartY + row * squareSize;
boardContainer.addChild(square);
}
}
// Add coordinate system labels
var coordinateContainer = game.addChild(new Container());
// Column labels (a-p for 16 columns)
var columnLabels = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'];
for (var col = 0; col < 16; col++) {
// Bottom row labels
var bottomLabel = new Text2(columnLabels[col], {
size: 32,
fill: 0xFFFFFF
});
bottomLabel.anchor.set(0.5, 0.5);
bottomLabel.x = boardStartX + col * squareSize + squareSize / 2;
bottomLabel.y = boardStartY + 8 * squareSize + 25;
coordinateContainer.addChild(bottomLabel);
// Top row labels
var topLabel = new Text2(columnLabels[col], {
size: 32,
fill: 0xFFFFFF
});
topLabel.anchor.set(0.5, 0.5);
topLabel.x = boardStartX + col * squareSize + squareSize / 2;
topLabel.y = boardStartY - 25;
coordinateContainer.addChild(topLabel);
}
// Row labels (8-1 from top to bottom)
for (var row = 0; row < 8; row++) {
var rowNumber = (8 - row).toString();
// Left side labels
var leftLabel = new Text2(rowNumber, {
size: 32,
fill: 0xFFFFFF
});
leftLabel.anchor.set(0.5, 0.5);
leftLabel.x = boardStartX - 25;
leftLabel.y = boardStartY + row * squareSize + squareSize / 2;
coordinateContainer.addChild(leftLabel);
// Right side labels
var rightLabel = new Text2(rowNumber, {
size: 32,
fill: 0xFFFFFF
});
rightLabel.anchor.set(0.5, 0.5);
rightLabel.x = boardStartX + 16 * squareSize + 25;
rightLabel.y = boardStartY + row * squareSize + squareSize / 2;
coordinateContainer.addChild(rightLabel);
}
// Initialize pieces with mixed layout - includes quenns, rooks, knights, 2 princesses, and 2 jesters
var basePieceSetup = [['summoner', 'knight', 'bishop', 'queen', 'king', 'queen', 'bishop', 'knight', 'rook', 'jester', 'knight', 'bishop', 'princess', 'princess', 'snake', 'jester'], ['pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn']];
var pieceSetup = shufflePiecesForNewRound(basePieceSetup);
// Create black pieces
for (var row = 0; row < 2; row++) {
for (var col = 0; col < 16; col++) {
var piece = new ChessPiece(pieceSetup[row][col], 'black', row, col);
pieces.push(piece);
gameBoard[row][col] = piece;
game.addChild(piece);
piece.updatePosition();
}
}
// Create white pieces
for (var row = 6; row < 8; row++) {
for (var col = 0; col < 16; col++) {
var pieceType = row === 6 ? 'pawn' : pieceSetup.whiteSetup[col];
var piece = new ChessPiece(pieceType, 'white', row, col);
pieces.push(piece);
gameBoard[row][col] = piece;
game.addChild(piece);
piece.updatePosition();
}
}
// Initialize king square
updateKingSquare();
// UI Elements
var turnText = new Text2('Your Turn', {
size: 80,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
LK.gui.top.addChild(turnText);
// Lives display - positioned behind chess pieces on the board
var whiteLivesContainer = new Container();
game.addChild(whiteLivesContainer);
var blackLivesContainer = new Container();
game.addChild(blackLivesContainer);
var whiteLivesText = new Text2('Your Lives: 4', {
size: 40,
fill: 0xFFFFFF
});
whiteLivesText.anchor.set(0.5, 0.5);
whiteLivesText.x = boardStartX + 16 * squareSize / 2;
whiteLivesText.y = boardStartY + 8 * squareSize + 50;
whiteLivesContainer.addChild(whiteLivesText);
var blackLivesText = new Text2('Enemy Lives: 4', {
size: 40,
fill: 0xFF0000
});
blackLivesText.anchor.set(0.5, 0.5);
blackLivesText.x = boardStartX + 16 * squareSize / 2;
blackLivesText.y = boardStartY - 50;
blackLivesContainer.addChild(blackLivesText);
// Lives visual indicators
var whiteLivesHearts = [];
var blackLivesHearts = [];
for (var i = 0; i < 4; i++) {
var whiteHeart = LK.getAsset('playerLifeBar', {
anchorX: 0.5,
anchorY: 0.5
});
whiteHeart.x = boardStartX + 16 * squareSize / 2 - 75 + i * 50;
whiteHeart.y = boardStartY + 8 * squareSize + 100;
whiteLivesContainer.addChild(whiteHeart);
whiteLivesHearts.push(whiteHeart);
var blackHeart = LK.getAsset('aiLifeBar', {
anchorX: 0.5,
anchorY: 0.5
});
blackHeart.x = boardStartX + 16 * squareSize / 2 - 75 + i * 50;
blackHeart.y = boardStartY - 100;
blackLivesContainer.addChild(blackHeart);
blackLivesHearts.push(blackHeart);
}
// Create stamina bar display
staminaContainer = new Container();
game.addChild(staminaContainer);
var staminaText = new Text2('Stamina', {
size: 40,
fill: 0xff69b4
});
staminaText.anchor.set(0.5, 0.5);
staminaText.x = boardStartX + 16 * squareSize - 100;
staminaText.y = boardStartY + 8 * squareSize + 150;
staminaContainer.addChild(staminaText);
for (var i = 0; i < maxStaminaPoints; i++) {
var staminaBar = LK.getAsset('staminaBar', {
anchorX: 0.5,
anchorY: 0.5
});
staminaBar.x = boardStartX + 16 * squareSize - 200 + i * 50;
staminaBar.y = boardStartY + 8 * squareSize + 200;
staminaContainer.addChild(staminaBar);
staminaBars.push(staminaBar);
}
// Create AI stamina bar display
var aiStaminaContainer = new Container();
game.addChild(aiStaminaContainer);
var aiStaminaText = new Text2('Enemy Stamina', {
size: 40,
fill: 0xff69b4
});
aiStaminaText.anchor.set(0.5, 0.5);
aiStaminaText.x = boardStartX + 16 * squareSize - 100;
aiStaminaText.y = boardStartY - 150;
aiStaminaContainer.addChild(aiStaminaText);
var aiStaminaBars = [];
for (var i = 0; i < aiMaxStaminaPoints; i++) {
var aiStaminaBar = LK.getAsset('staminaBar', {
anchorX: 0.5,
anchorY: 0.5
});
aiStaminaBar.x = boardStartX + 16 * squareSize - 200 + i * 50;
aiStaminaBar.y = boardStartY - 100;
aiStaminaContainer.addChild(aiStaminaBar);
aiStaminaBars.push(aiStaminaBar);
}
updateStaminaDisplay();
// Timer display elements
var whiteTimerText = new Text2('1:00', {
size: 60,
fill: 0xFFFFFF
});
whiteTimerText.anchor.set(0.5, 0.5);
whiteTimerText.x = boardStartX + 0 * squareSize + squareSize / 2; // Align with "a" column
whiteTimerText.y = boardStartY + 8 * squareSize + 75; // Below the bottom "a" label
game.addChild(whiteTimerText);
var blackTimerText = new Text2('1:00', {
size: 60,
fill: 0xFF0000
});
blackTimerText.anchor.set(0.5, 0.5);
blackTimerText.x = boardStartX + 0 * squareSize + squareSize / 2; // Align with "a" column
blackTimerText.y = boardStartY - 75; // Above the top "a" label
game.addChild(blackTimerText);
// Initialize and start timer after UI elements are created
updateTimerDisplay();
startTimer();
function getPieceAt(row, col) {
if (row < 0 || row > 7 || col < 0 || col > 15) {
return null;
}
return gameBoard[row][col];
}
function isPathClear(fromRow, fromCol, toRow, toCol) {
var rowStep = toRow > fromRow ? 1 : toRow < fromRow ? -1 : 0;
var colStep = toCol > fromCol ? 1 : toCol < fromCol ? -1 : 0;
var currentRow = fromRow + rowStep;
var currentCol = fromCol + colStep;
while (currentRow !== toRow || currentCol !== toCol) {
if (getPieceAt(currentRow, currentCol)) {
return false;
}
currentRow += rowStep;
currentCol += colStep;
}
return true;
}
function getSquareFromPosition(x, y) {
var col = Math.floor((x - boardStartX) / squareSize);
var row = Math.floor((y - boardStartY) / squareSize);
if (row >= 0 && row < 8 && col >= 0 && col < 16) {
return {
row: row,
col: col
};
}
return null;
}
function clearHighlights() {
// Smooth fade-out for highlights
for (var i = 0; i < highlightSquares.length; i++) {
var highlight = highlightSquares[i];
tween(highlight, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
if (!highlight.destroyed) highlight.destroy();
}
});
}
highlightSquares = [];
for (var i = 0; i < validMoveSquares.length; i++) {
var highlight = validMoveSquares[i];
// Immediately destroy flashing green squares instead of animating them out
if (!highlight.destroyed) highlight.destroy();
}
validMoveSquares = [];
for (var i = 0; i < escapeSquares.length; i++) {
var highlight = escapeSquares[i];
tween(highlight, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
if (!highlight.destroyed) highlight.destroy();
}
});
}
escapeSquares = [];
}
function createCaptureParticles(x, y, color) {
// Create sparkling particle effect for captures
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('poisonEffect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
tint: color || 0xFFD700,
alpha: 1.0
});
particle.x = x;
particle.y = y;
game.addChild(particle);
// Animate particles spreading out
var angle = i / 8 * Math.PI * 2;
var distance = 60 + Math.random() * 40;
var targetX = x + Math.cos(angle) * distance;
var targetY = y + Math.sin(angle) * distance;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1,
rotation: Math.random() * Math.PI * 2
}, {
duration: 800 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (!particle.destroyed) particle.destroy();
}
});
}
}
function createExplosionEffect(centerRow, centerCol) {
// Create explosion effect in 3x3 area around the center position
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
var explosionRow = centerRow + dr;
var explosionCol = centerCol + dc;
// Check bounds
if (explosionRow >= 0 && explosionRow < 8 && explosionCol >= 0 && explosionCol < 16) {
var explosionX = boardStartX + explosionCol * squareSize + squareSize / 2;
var explosionY = boardStartY + explosionRow * squareSize + squareSize / 2;
// Create multiple explosion particles for each square
for (var i = 0; i < 12; i++) {
var explosionParticle = LK.getAsset('poisonEffect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6 + Math.random() * 0.4,
scaleY: 0.6 + Math.random() * 0.4,
tint: 0xFF6600 + Math.floor(Math.random() * 0x9900),
// Orange to red explosion colors
alpha: 0.9
});
explosionParticle.x = explosionX + (Math.random() - 0.5) * 60;
explosionParticle.y = explosionY + (Math.random() - 0.5) * 60;
game.addChild(explosionParticle);
// Animate explosion particles
var explosionAngle = Math.random() * Math.PI * 2;
var explosionDistance = 80 + Math.random() * 60;
var explosionTargetX = explosionX + Math.cos(explosionAngle) * explosionDistance;
var explosionTargetY = explosionY + Math.sin(explosionAngle) * explosionDistance;
tween(explosionParticle, {
x: explosionTargetX,
y: explosionTargetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1,
rotation: Math.random() * Math.PI * 4
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (!explosionParticle.destroyed) explosionParticle.destroy();
}
});
}
// Create bright flash effect in center of explosion
if (dr === 0 && dc === 0) {
var flash = LK.getAsset('highlightSquare', {
anchorX: 0,
anchorY: 0,
alpha: 0.8,
tint: 0xFF3300,
scaleX: 1.5,
scaleY: 1.5
});
flash.x = boardStartX + explosionCol * squareSize - squareSize * 0.25;
flash.y = boardStartY + explosionRow * squareSize - squareSize * 0.25;
game.addChild(flash);
tween(flash, {
alpha: 0,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (!flash.destroyed) flash.destroy();
}
});
}
}
}
}
}
function updateKingSquare() {
// Remove existing king squares
if (kingSquare) {
kingSquare.destroy();
kingSquare = null;
}
if (blackKingSquare) {
blackKingSquare.destroy();
blackKingSquare = null;
}
// Find the white king and create purple square under it
var whiteKing = findKing('white');
if (whiteKing) {
kingSquare = LK.getAsset('kingSquare', {
anchorX: 0,
anchorY: 0,
alpha: 0.8
});
kingSquare.x = boardStartX + whiteKing.col * squareSize;
kingSquare.y = boardStartY + whiteKing.row * squareSize;
boardContainer.addChild(kingSquare);
}
// Find the black king and create purple square under it
var blackKing = findKing('black');
if (blackKing) {
blackKingSquare = LK.getAsset('kingSquare', {
anchorX: 0,
anchorY: 0,
alpha: 0.8
});
blackKingSquare.x = boardStartX + blackKing.col * squareSize;
blackKingSquare.y = boardStartY + blackKing.row * squareSize;
boardContainer.addChild(blackKingSquare);
}
}
function highlightSquare(row, col, color) {
var assetName = 'validMoveSquare';
if (color === 'blue') {
assetName = 'highlightSquare';
} else if (color === 'yellow') {
assetName = 'escapeSquare';
}
var highlight = LK.getAsset(assetName, {
anchorX: 0,
anchorY: 0,
alpha: 0.0,
scaleX: 0.8,
scaleY: 0.8
});
highlight.x = boardStartX + col * squareSize;
highlight.y = boardStartY + row * squareSize;
game.addChild(highlight);
// Smooth fade-in and scale animation for highlights
tween(highlight, {
alpha: 0.7,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeOut
});
// Add subtle pulsing animation for valid moves
if (color === 'green') {
var _pulseTween = function pulseTween() {
tween(highlight, {
alpha: 0.4
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(highlight, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: _pulseTween
});
}
});
};
_pulseTween();
}
if (color === 'blue') {
highlightSquares.push(highlight);
} else if (color === 'yellow') {
escapeSquares.push(highlight);
} else {
validMoveSquares.push(highlight);
}
}
function showValidMoves(piece) {
validMoves = [];
var playerInCheck = isInCheck(piece.pieceColor);
// Always highlight the selected piece position in blue first
highlightSquare(piece.row, piece.col, 'blue');
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (piece.canMoveTo(row, col)) {
// If in check, only allow moves that escape check
if (playerInCheck) {
if (canEscapeCheck(piece, row, col)) {
validMoves.push({
row: row,
col: col
});
// Highlight escape move in yellow when in check
highlightSquare(row, col, 'yellow');
}
} else {
validMoves.push({
row: row,
col: col
});
highlightSquare(row, col, 'green');
}
}
}
}
}
function movePiece(piece, newRow, newCol) {
var capturedPiece = getPieceAt(newRow, newCol);
var isCapture = capturedPiece !== null;
// Remove piece from old position
gameBoard[piece.row][piece.col] = null;
// Check for king auto-capture of adjacent snake before normal capture handling
if (piece.pieceType === 'snake') {
// Check if any enemy king is adjacent to the snake's new position
var enemyColor = piece.pieceColor === 'white' ? 'black' : 'white';
var enemyKing = findKing(enemyColor);
if (enemyKing) {
var kingRowDiff = Math.abs(enemyKing.row - newRow);
var kingColDiff = Math.abs(enemyKing.col - newCol);
// If king is adjacent (including diagonal), king automatically captures snake
if (kingRowDiff <= 1 && kingColDiff <= 1) {
// King auto-captures the snake
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === piece) {
pieces[i].destroy();
pieces.splice(i, 1);
break;
}
}
// Move king to snake position
gameBoard[enemyKing.row][enemyKing.col] = null;
enemyKing.row = newRow;
enemyKing.col = newCol;
gameBoard[enemyKing.row][enemyKing.col] = enemyKing;
enemyKing.updatePosition(true);
updateKingSquare();
LK.getSound('capture').play();
turnText.setText(enemyKing.pieceColor.charAt(0).toUpperCase() + enemyKing.pieceColor.slice(1) + ' King Auto-Captures Snake!');
// Reset snake state
currentSnakePiece = null;
snakeMovesRemaining = 0;
activeSnakePiece = null;
// Switch turns
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
return;
}
}
}
// Handle jester jump captures
if (piece.pieceType === 'jester') {
var rowDiff = newRow - piece.row;
var colDiff = newCol - piece.col;
var absRowDiff = Math.abs(rowDiff);
var absColDiff = Math.abs(colDiff);
// Check if this is a 2-square move (potential jump)
if (Math.max(absRowDiff, absColDiff) === 2) {
// Calculate middle square position
var middleRow = piece.row + (rowDiff > 0 ? 1 : rowDiff < 0 ? -1 : 0);
var middleCol = piece.col + (colDiff > 0 ? 1 : colDiff < 0 ? -1 : 0);
var jumpedPiece = getPieceAt(middleRow, middleCol);
// If there's an enemy piece in the middle, capture it (jump capture)
if (jumpedPiece && jumpedPiece.pieceColor !== piece.pieceColor) {
// Remove jumped piece with effects
var jumpCaptureX = boardStartX + middleCol * squareSize + squareSize / 2;
var jumpCaptureY = boardStartY + middleRow * squareSize + squareSize / 2;
var jumpParticleColor = jumpedPiece.pieceColor === 'white' ? 0xFFD700 : 0xFF4444;
createCaptureParticles(jumpCaptureX, jumpCaptureY, jumpParticleColor);
// Animate jumped piece removal
tween(jumpedPiece, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5,
rotation: Math.PI
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
if (!jumpedPiece.destroyed) {
jumpedPiece.destroy();
}
}
});
// Play special jester capture sound
LK.getSound('jesterCapture').play();
// Remove jumped piece from game state
gameBoard[middleRow][middleCol] = null;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === jumpedPiece) {
pieces.splice(i, 1);
break;
}
}
}
}
}
// Remove captured piece with visual effects
if (capturedPiece) {
// Create particle effects at capture position
var captureX = boardStartX + newCol * squareSize + squareSize / 2;
var captureY = boardStartY + newRow * squareSize + squareSize / 2;
var particleColor = capturedPiece.pieceColor === 'white' ? 0xFFD700 : 0xFF4444;
createCaptureParticles(captureX, captureY, particleColor);
// Animate captured piece before removal
tween(capturedPiece, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5,
rotation: Math.PI
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
if (!capturedPiece.destroyed) {
capturedPiece.destroy();
}
}
});
// Check if captured piece is a snake in the middle of its multi-move sequence
if (capturedPiece.pieceType === 'snake' && capturedPiece.snakeMovesThisTurn > 0 && capturedPiece === activeSnakePiece) {
// Snake was captured before completing its moves - put on cooldown and reset sequence
capturedPiece.snakeOnCooldown = true;
capturedPiece.snakeMovesThisTurn = 0;
capturedPiece.snakeLastMoveDirection = null;
// Reset global snake state
currentSnakePiece = null;
snakeMovesRemaining = 0;
activeSnakePiece = null;
}
// Handle king capture - check if king can counter-capture
if (capturedPiece.pieceType === 'king') {
// Check if captured king can counter-capture the attacking piece
var canCounterCapture = capturedPiece && piece && capturedPiece.canMoveTo(piece.row, piece.col) && isKingSafeAt(capturedPiece, piece.row, piece.col);
if (canCounterCapture) {
// Auto counter-capture: king takes the attacking piece
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === piece) {
pieces[i].destroy();
pieces.splice(i, 1);
break;
}
}
// Move king to attacking piece position
gameBoard[capturedPiece.row][capturedPiece.col] = null;
capturedPiece.row = piece.row;
capturedPiece.col = piece.col;
gameBoard[capturedPiece.row][capturedPiece.col] = capturedPiece;
capturedPiece.updatePosition(true);
updateKingSquare();
LK.getSound('capture').play();
// Apply 1 damage to the king's side for escaping
if (capturedPiece.pieceColor === 'white') {
whiteKingLives--;
activateAbility('white', whiteKingLives);
} else {
blackKingLives--;
activateAbility('black', blackKingLives);
}
updateLivesDisplay();
turnText.setText(capturedPiece.pieceColor.charAt(0).toUpperCase() + capturedPiece.pieceColor.slice(1) + ' King Counter-Attacks! 1 Life Lost for Escaping!');
return;
} else {
// Reduce king lives
if (capturedPiece.pieceColor === 'white') {
whiteKingLives--;
activateAbility('white', whiteKingLives);
} else {
blackKingLives--;
activateAbility('black', blackKingLives);
}
// Update lives display
updateLivesDisplay();
// Check if king is out of lives (restart game when lives reach 0)
if (whiteKingLives <= 0 || blackKingLives <= 0) {
var winner = whiteKingLives <= 0 ? 'Black' : 'White';
turnText.setText(winner + (winner === 'Black' ? ' side won.' : ' Wins! King Defeated! New round begins with shuffled pieces!'));
LK.setTimeout(function () {
restartGame();
}, 2000);
return;
}
}
} else {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === capturedPiece) {
pieces[i].destroy();
pieces.splice(i, 1);
break;
}
}
// Add stamina point for capturing (exclude Queen and Snake captures)
if (capturedPiece.pieceType !== 'queen' && capturedPiece.pieceType !== 'snake') {
if (piece.pieceColor === 'white' && staminaPoints < maxStaminaPoints) {
staminaPoints++;
updateStaminaDisplay();
} else if (piece.pieceColor === 'black' && aiStaminaPoints < aiMaxStaminaPoints) {
aiStaminaPoints++;
}
}
}
}
// Handle castling before moving the king
if (piece.pieceType === 'king' && Math.abs(newCol - piece.col) === 2) {
var rookCol, rookNewCol;
if (newCol > piece.col) {
// King-side castling
rookCol = 15;
rookNewCol = newCol - 1;
} else {
// Queen-side castling
rookCol = 0;
rookNewCol = newCol + 1;
}
var rook = getPieceAt(piece.row, rookCol);
if (rook) {
// Move rook
gameBoard[rook.row][rook.col] = null;
rook.col = rookNewCol;
rook.hasMoved = true;
gameBoard[rook.row][rook.col] = rook;
rook.updatePosition(true);
}
}
// Move piece to new position
piece.row = newRow;
piece.col = newCol;
piece.hasMoved = true;
gameBoard[newRow][newCol] = piece;
piece.updatePosition(true);
// Handle stamina cost for Queen and Snake moves
// For snake, only deduct stamina on the first move of the sequence
if (piece.pieceType === 'queen' || piece.pieceType === 'snake' && piece.snakeMovesThisTurn === 0) {
if (piece.pieceColor === 'white' && staminaPoints > 0) {
staminaPoints--;
updateStaminaDisplay();
} else if (piece.pieceColor === 'black' && aiStaminaPoints > 0) {
aiStaminaPoints--;
}
}
// Update king square if a king moved
if (piece.pieceType === 'king') {
updateKingSquare();
}
// Play sound
if (isCapture) {
if (piece.pieceType === 'jester') {
LK.getSound('jesterCapture').play();
} else {
LK.getSound('capture').play();
}
} else {
LK.getSound('move').play();
}
// Check for pawn promotion
if (piece.pieceType === 'pawn' && (newRow === 0 || newRow === 7)) {
promotePawn(piece);
}
// Handle summoner move counting and summoning
if (piece.pieceType === 'summoner') {
piece.summonerMoveCount++;
piece.hasSummoned = false; // Reset summoned flag when moving
if (piece.summonerMoveCount >= 3) {
// Execute summoning after move animation completes
LK.setTimeout(function () {
piece.executeSummon();
}, 400);
}
}
// Handle snake multi-move mechanics
if (piece.pieceType === 'snake') {
// Set this snake as the active snake if starting its turn
if (piece.snakeMovesThisTurn === 0) {
activeSnakePiece = piece;
var rowDiff = newRow - piece.row;
var colDiff = newCol - piece.col;
piece.snakeLastMoveDirection = {
row: rowDiff === 0 ? 0 : rowDiff > 0 ? 1 : -1,
col: colDiff === 0 ? 0 : colDiff > 0 ? 1 : -1
};
}
// Check if poisoning will occur at the new position after the move
var willPoison = false;
var poisonPositions = [{
row: newRow - 1,
col: newCol - 1
}, {
row: newRow - 1,
col: newCol
}, {
row: newRow - 1,
col: newCol + 1
}, {
row: newRow,
col: newCol - 1
}, {
row: newRow,
col: newCol + 1
}, {
row: newRow + 1,
col: newCol - 1
}, {
row: newRow + 1,
col: newCol
}, {
row: newRow + 1,
col: newCol + 1
}];
for (var i = 0; i < poisonPositions.length; i++) {
var pos = poisonPositions[i];
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
var pieceAtPos = getPieceAt(pos.row, pos.col);
if (pieceAtPos && pieceAtPos.pieceColor !== piece.pieceColor) {
willPoison = true;
break;
}
}
}
piece.snakeMovesThisTurn++;
var maxMoves = willPoison ? 2 : piece.maxSnakeMoves;
snakeMovesRemaining = maxMoves - piece.snakeMovesThisTurn;
currentSnakePiece = piece;
// Apply poison to pieces around the snake's new position
applyPoisonBehindSnake(piece, newRow, newCol);
// Handle additional stamina cost for poisoning
if (willPoison) {
// Deduct additional stamina for poisoning
if (piece.pieceColor === 'white' && staminaPoints > 0) {
staminaPoints--;
updateStaminaDisplay();
} else if (piece.pieceColor === 'black' && aiStaminaPoints > 0) {
aiStaminaPoints--;
}
}
// If poisoning occurred, immediately switch turns after 2 moves
if (willPoison) {
// Snake completed poisoning moves - put on cooldown and reset
piece.snakeMovesThisTurn = 0;
piece.snakeOnCooldown = true;
piece.snakeLastMoveDirection = null;
currentSnakePiece = null;
snakeMovesRemaining = 0;
activeSnakePiece = null; // Reset active snake
turnText.setText(piece.pieceColor.charAt(0).toUpperCase() + piece.pieceColor.slice(1) + ' Snake poisoned enemies! Turn passes to opponent.');
} else {
// Snake eats piece at each stop - already handled by capture logic above
// If snake has more moves, don't switch turns yet
if (piece.snakeMovesThisTurn < maxMoves) {
var moveNames = ['', 'First', 'Second', 'Third'];
turnText.setText(piece.pieceColor.charAt(0).toUpperCase() + piece.pieceColor.slice(1) + ' Snake - ' + moveNames[piece.snakeMovesThisTurn] + ' move complete, ' + snakeMovesRemaining + ' moves remaining');
// Clear current selection to allow new move
clearHighlights();
selectedPiece = null;
return; // Don't switch turns
}
// Snake completed all 3 moves - put on cooldown and reset
piece.snakeMovesThisTurn = 0;
piece.snakeOnCooldown = true;
piece.snakeLastMoveDirection = null;
currentSnakePiece = null;
snakeMovesRemaining = 0;
activeSnakePiece = null; // Reset active snake
}
}
// Switch turns
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
// Reset timer for new turn
currentTurnStartTime = Date.now();
// Reduce poison turns and remove snake cooldown for all pieces of the new current player
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === currentPlayer) {
pieces[i].reducePoisonTurns();
// Remove snake cooldown when it becomes this player's turn
if (pieces[i].pieceType === 'snake') {
pieces[i].snakeOnCooldown = false;
}
}
}
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + '\'s Turn');
// Check for check/checkmate
if (isInCheck(currentPlayer)) {
// In check situations, look for defensive moves: capturing the threatening piece, blocking, or king escape
var king = findKing(currentPlayer);
var threateningPiece = findThreateningPiece(king);
var canKingCounterCapture = false;
var canDefend = false;
var defendingPiece = null;
var defenseRow = -1;
var defenseCol = -1;
// Check for defensive options when in check
if (threateningPiece) {
// Priority 1: King can directly counter-capture the threatening piece
if (king && king.canMoveTo(threateningPiece.row, threateningPiece.col) && isKingSafeAt(king, threateningPiece.row, threateningPiece.col)) {
canKingCounterCapture = true;
canDefend = true;
defendingPiece = king;
defenseRow = threateningPiece.row;
defenseCol = threateningPiece.col;
} else {
// Priority 2: Any other piece can capture the threatening piece to protect the king
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === currentPlayer && piece !== king && piece.canMoveTo(threateningPiece.row, threateningPiece.col)) {
// Test if this capture would resolve the check
if (canEscapeCheck(piece, threateningPiece.row, threateningPiece.col)) {
canDefend = true;
defendingPiece = piece;
defenseRow = threateningPiece.row;
defenseCol = threateningPiece.col;
break;
}
}
}
// Priority 3: If no capture is possible, look for blocking moves or king escape moves
if (!canDefend) {
// Check if any piece can block the attack path or if king can escape
var allDefensiveMoves = getAllValidMoves(currentPlayer);
canDefend = allDefensiveMoves.length > 0;
}
}
}
// If defensive action is available, execute it
if (canDefend && defendingPiece) {
// Auto-defense: piece protects king by capturing the threatening piece
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === threateningPiece) {
pieces[i].destroy();
pieces.splice(i, 1);
break;
}
}
// Move defending piece to capture position
gameBoard[defendingPiece.row][defendingPiece.col] = null;
defendingPiece.row = defenseRow;
defendingPiece.col = defenseCol;
gameBoard[defendingPiece.row][defendingPiece.col] = defendingPiece;
defendingPiece.updatePosition(true);
if (defendingPiece.pieceType === 'king') {
updateKingSquare();
// King counter-capture - game continues without penalty when king directly captures threat
if (canKingCounterCapture) {
turnText.setText(defendingPiece.pieceColor.charAt(0).toUpperCase() + defendingPiece.pieceColor.slice(1) + ' King Counter-Captures Threatening Piece!');
} else {
// Apply 1 damage to king's side for escaping by counter-capture in defensive situations
if (defendingPiece.pieceColor === 'white') {
whiteKingLives--;
activateAbility('white', whiteKingLives);
} else {
blackKingLives--;
activateAbility('black', blackKingLives);
}
updateLivesDisplay();
turnText.setText(defendingPiece.pieceColor.charAt(0).toUpperCase() + defendingPiece.pieceColor.slice(1) + ' King Defends! 1 Life Lost for Escaping!');
}
} else {
var pieceTitle = defendingPiece.pieceType.charAt(0).toUpperCase() + defendingPiece.pieceType.slice(1);
turnText.setText(defendingPiece.pieceColor.charAt(0).toUpperCase() + defendingPiece.pieceColor.slice(1) + ' ' + pieceTitle + ' Protects King by Capturing Threat!');
}
LK.getSound('capture').play();
// Continue game - switch turns
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + '\'s Turn');
gameState = 'playing';
// Trigger AI move if it's black's turn
if (currentPlayer === 'black') {
makeAIMove();
}
return;
}
// Check if player has no defensive options (no escape moves) - comprehensive defense check
if (!hasValidEscapeMoves(currentPlayer)) {
// Look for all possible defensive options: king counter-capture, piece protection, blocking
var king = findKing(currentPlayer);
var threateningPiece = findThreateningPiece(king);
var canDefendKing = false;
var protectingPiece = null;
if (threateningPiece) {
// Option 1: King can directly counter-capture the threatening piece
if (king && king.canMoveTo(threateningPiece.row, threateningPiece.col) && isKingSafeAt(king, threateningPiece.row, threateningPiece.col)) {
canDefendKing = true;
protectingPiece = king;
} else {
// Option 2: Any other piece can protect the king by capturing the threatening piece
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === currentPlayer && piece !== king && piece.canMoveTo(threateningPiece.row, threateningPiece.col)) {
if (canEscapeCheck(piece, threateningPiece.row, threateningPiece.col)) {
canDefendKing = true;
protectingPiece = piece;
break;
}
}
}
// Option 3: Any piece can block the attack path (if attack is not adjacent)
if (!canDefendKing && king && threateningPiece) {
var blockingMoves = getAllValidMoves(currentPlayer);
for (var i = 0; i < blockingMoves.length; i++) {
if (canEscapeCheck(blockingMoves[i].piece, blockingMoves[i].toRow, blockingMoves[i].toCol)) {
canDefendKing = true;
protectingPiece = blockingMoves[i].piece;
break;
}
}
}
}
}
if (canDefendKing && protectingPiece && threateningPiece) {
// Execute king protection with explosion effect
var threateningPieceRow = threateningPiece.row;
var threateningPieceCol = threateningPiece.col;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === threateningPiece) {
pieces[i].destroy();
pieces.splice(i, 1);
break;
}
}
// Create explosion effect where threatening piece was destroyed
createExplosionEffect(threateningPieceRow, threateningPieceCol);
// Move protecting piece to capture position
gameBoard[protectingPiece.row][protectingPiece.col] = null;
protectingPiece.row = threateningPiece.row;
protectingPiece.col = threateningPiece.col;
gameBoard[protectingPiece.row][protectingPiece.col] = protectingPiece;
protectingPiece.updatePosition(true);
if (protectingPiece.pieceType === 'king') {
updateKingSquare();
}
LK.getSound('capture').play();
var pieceTitle = protectingPiece.pieceType.charAt(0).toUpperCase() + protectingPiece.pieceType.slice(1);
turnText.setText(protectingPiece.pieceColor.charAt(0).toUpperCase() + protectingPiece.pieceColor.slice(1) + ' ' + pieceTitle + ' Protects King! Game Continues!');
// Switch turns and continue game
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + '\'s Turn');
gameState = 'playing';
// Trigger AI move if it's black's turn
if (currentPlayer === 'black') {
makeAIMove();
}
return;
}
// Checkmate situation - king is trapped and cannot escape, lose 2 lives and remove only the checking piece
if (currentPlayer === 'white') {
whiteKingLives -= 2;
if (whiteKingLives < 0) whiteKingLives = 0;
} else {
blackKingLives -= 2;
if (blackKingLives < 0) blackKingLives = 0;
}
// Remove only the piece that is currently checking the king (the threatening piece)
var king = findKing(currentPlayer);
var checkingPiece = findThreateningPiece(king);
if (checkingPiece) {
var checkingPieceRow = checkingPiece.row;
var checkingPieceCol = checkingPiece.col;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === checkingPiece) {
gameBoard[checkingPiece.row][checkingPiece.col] = null;
checkingPiece.destroy();
pieces.splice(i, 1);
break;
}
}
// Create explosion effect where checking piece was destroyed
createExplosionEffect(checkingPieceRow, checkingPieceCol);
}
updateLivesDisplay();
// Check if king is out of lives (restart game when lives reach 0)
if (whiteKingLives <= 0 || blackKingLives <= 0) {
var winner = whiteKingLives <= 0 ? 'Black' : 'White';
turnText.setText(winner + (winner === 'Black' ? ' side won.' : ' Wins! King Out of Lives!'));
LK.setTimeout(function () {
restartGame();
}, 2000);
return;
}
turnText.setText('Checkmate! ' + currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + ' King loses 2 lives! Threatening piece destroyed!');
// Switch turns and continue game
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
LK.setTimeout(function () {
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + '\'s Turn');
gameState = 'playing';
// Trigger AI move if it's black's turn
if (currentPlayer === 'black') {
makeAIMove();
}
}, 2000);
return;
}
// Reduce king lives when in check but not checkmated
if (currentPlayer === 'white') {
whiteKingLives--;
activateAbility('white', whiteKingLives);
} else {
blackKingLives--;
activateAbility('black', blackKingLives);
}
updateLivesDisplay();
// Check if king is out of lives (restart game when lives reach 0)
if (whiteKingLives <= 0 || blackKingLives <= 0) {
var winner = whiteKingLives <= 0 ? 'Black' : 'White';
turnText.setText(winner + (winner === 'Black' ? ' side won.' : ' Wins! King Out of Lives! New round begins with shuffled pieces!'));
LK.setTimeout(function () {
restartGame();
}, 2000);
return;
}
gameState = 'check';
LK.getSound('check').play();
// Add dramatic check visual effect
LK.effects.flashScreen(0xFF0000, 800);
var kingInCheck = findKing(currentPlayer);
if (kingInCheck) {
// Make the king flash red
tween(kingInCheck, {
tint: 0xFF6666
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(kingInCheck, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
var currentLives = currentPlayer === 'white' ? whiteKingLives : blackKingLives;
if (currentLives === 0) {
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + ' in Check! FINAL LIFE - Must Escape or Lose!');
} else {
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + ' in Check! Lives: ' + currentLives + ' - Must Escape!');
}
} else if (isStalemate(currentPlayer)) {
gameState = 'stalemate';
turnText.setText('Stalemate!');
LK.setTimeout(function () {
restartGame();
}, 2000);
} else {
gameState = 'playing';
}
// Trigger AI move if it's black's turn
if (currentPlayer === 'black' && (gameState === 'playing' || gameState === 'check')) {
makeAIMove();
}
}
function getPieceValue(pieceType) {
switch (pieceType) {
case 'pawn':
return 1;
case 'bishop':
return 3;
case 'knight':
return 3;
case 'rook':
return 5;
case 'queen':
return 9;
case 'snake':
return 6;
// Higher value due to special abilities
case 'jester':
return 4;
// Moderate value for unique movement
case 'princess':
return 8;
case 'summoner':
return 8;
// Same value as princess
case 'king':
return 0;
default:
return 0;
}
}
function promotePawn(pawn) {
// Promote pawn to quenn (most powerful standard piece available)
var originalRow = pawn.row;
var originalCol = pawn.col;
var originalColor = pawn.pieceColor;
// Remove old pawn
pawn.destroy();
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === pawn) {
pieces.splice(i, 1);
break;
}
}
gameBoard[originalRow][originalCol] = null;
// Create new queen
var newQueen = new ChessPiece('queen', originalColor, originalRow, originalCol);
pieces.push(newQueen);
gameBoard[originalRow][originalCol] = newQueen;
game.addChild(newQueen);
newQueen.updatePosition();
}
function findKing(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceType === 'king' && pieces[i].pieceColor === color) {
return pieces[i];
}
}
return null;
}
function isInCheck(color) {
var king = findKing(color);
if (!king) return false;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor !== color && piece.canMoveTo(king.row, king.col)) {
return true;
}
}
return false;
}
function isCheckmate(color) {
if (!isInCheck(color)) return false;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === color) {
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (piece.canMoveTo(row, col)) {
// Simulate move
var originalRow = piece.row;
var originalCol = piece.col;
var capturedPiece = getPieceAt(row, col);
gameBoard[originalRow][originalCol] = null;
gameBoard[row][col] = piece;
piece.row = row;
piece.col = col;
var stillInCheck = isInCheck(color);
// Restore position
piece.row = originalRow;
piece.col = originalCol;
gameBoard[originalRow][originalCol] = piece;
gameBoard[row][col] = capturedPiece;
if (!stillInCheck) {
return false;
}
}
}
}
}
}
return true;
}
function isStalemate(color) {
if (isInCheck(color)) return false;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === color) {
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (piece.canMoveTo(row, col)) {
return false;
}
}
}
}
}
return true;
}
function getAllValidMoves(color) {
var allMoves = [];
var playerInCheck = isInCheck(color);
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === color) {
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (piece.canMoveTo(row, col)) {
// If in check, only allow moves that escape check
if (playerInCheck) {
if (canEscapeCheck(piece, row, col)) {
allMoves.push({
piece: piece,
toRow: row,
toCol: col
});
}
} else {
allMoves.push({
piece: piece,
toRow: row,
toCol: col
});
}
}
}
}
}
}
return allMoves;
}
function makeAIMove() {
if (currentPlayer !== 'black' || gameState !== 'playing' && gameState !== 'check') return;
// If there's an active snake continuing its multi-move sequence, prioritize it
if (activeSnakePiece && activeSnakePiece.pieceColor === 'black' && snakeMovesRemaining > 0) {
var snakeMoves = [];
// Get valid moves only for the active snake
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (activeSnakePiece.canMoveTo(row, col)) {
snakeMoves.push({
piece: activeSnakePiece,
toRow: row,
toCol: col
});
}
}
}
if (snakeMoves.length > 0) {
var bestSnakeMove = chooseBestAIMove(snakeMoves);
if (bestSnakeMove) {
LK.setTimeout(function () {
movePiece(bestSnakeMove.piece, bestSnakeMove.toRow, bestSnakeMove.toCol);
// Continue snake moves if still the AI's turn and snake has moves left
if (currentSnakePiece && currentSnakePiece.pieceColor === 'black' && snakeMovesRemaining > 0 && currentPlayer === 'black') {
LK.setTimeout(function () {
makeAIMove(); // Recursive call for next snake move
}, 800);
}
}, 800);
return;
}
}
}
var validAIMoves = getAllValidMoves('black');
// Check if AI has no valid moves but can defend king
if (validAIMoves.length === 0 && isInCheck('black')) {
var blackKing = findKing('black');
var threateningPiece = findThreateningPiece(blackKing);
var canDefendAI = false;
var defendingAIPiece = null;
var defenseAIRow = -1;
var defenseAICol = -1;
// Look for defensive options for AI king
if (threateningPiece) {
// Priority 1: AI king can directly counter-capture the threatening piece
if (blackKing && blackKing.canMoveTo(threateningPiece.row, threateningPiece.col) && isKingSafeAt(blackKing, threateningPiece.row, threateningPiece.col)) {
canDefendAI = true;
defendingAIPiece = blackKing;
defenseAIRow = threateningPiece.row;
defenseAICol = threateningPiece.col;
} else {
// Priority 2: Any other AI piece can protect the king by capturing the threatening piece
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'black' && piece !== blackKing && piece.canMoveTo(threateningPiece.row, threateningPiece.col)) {
// Test if this capture would protect the AI king
if (canEscapeCheck(piece, threateningPiece.row, threateningPiece.col)) {
canDefendAI = true;
defendingAIPiece = piece;
defenseAIRow = threateningPiece.row;
defenseAICol = threateningPiece.col;
break;
}
}
}
}
}
if (canDefendAI) {
// AI defense: piece protects king by capturing the threatening piece
LK.setTimeout(function () {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === threateningPiece) {
pieces[i].destroy();
pieces.splice(i, 1);
break;
}
}
// Move AI defending piece to capture position
gameBoard[defendingAIPiece.row][defendingAIPiece.col] = null;
defendingAIPiece.row = defenseAIRow;
defendingAIPiece.col = defenseAICol;
gameBoard[defendingAIPiece.row][defendingAIPiece.col] = defendingAIPiece;
defendingAIPiece.updatePosition(true);
if (defendingAIPiece.pieceType === 'king') {
updateKingSquare();
// AI King counter-capture - game continues normally
if (defendingAIPiece === blackKing) {
turnText.setText('Black King Protects Itself! Game Continues!');
} else {
// Apply 1 damage to AI king for defensive action in extreme situations
blackKingLives--;
activateAbility('black', blackKingLives);
updateLivesDisplay();
turnText.setText('Black King Defends! 1 Life Lost for Escaping!');
}
} else {
var pieceTitle = defendingAIPiece.pieceType.charAt(0).toUpperCase() + defendingAIPiece.pieceType.slice(1);
turnText.setText('Black ' + pieceTitle + ' Protects King by Capturing Threat!');
}
LK.getSound('capture').play();
// Continue game - switch turns
currentPlayer = 'white';
turnText.setText('Your Turn');
gameState = 'playing';
}, 800);
return;
} else {
// No auto-capture possible and no valid escape moves - checkmate, lose 2 lives and remove only the checking piece
blackKingLives -= 2;
if (blackKingLives < 0) blackKingLives = 0;
// Remove only the piece that is currently checking the AI king (the threatening piece)
var blackKing = findKing('black');
var checkingPiece = findThreateningPiece(blackKing);
if (checkingPiece) {
var checkingPieceRow = checkingPiece.row;
var checkingPieceCol = checkingPiece.col;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i] === checkingPiece) {
gameBoard[checkingPiece.row][checkingPiece.col] = null;
checkingPiece.destroy();
pieces.splice(i, 1);
break;
}
}
// Create explosion effect where checking piece was destroyed
createExplosionEffect(checkingPieceRow, checkingPieceCol);
}
updateLivesDisplay();
// Check if AI king is out of lives
if (blackKingLives <= 0) {
turnText.setText('White Wins! AI King Out of Lives! New round begins with shuffled pieces!');
LK.setTimeout(function () {
restartGame();
}, 2000);
return;
}
turnText.setText('AI Checkmate! Black King loses 2 lives! Threatening piece destroyed!');
// Switch turns and continue game
currentPlayer = 'white';
LK.setTimeout(function () {
turnText.setText('Your Turn');
gameState = 'playing';
}, 2000);
return;
}
}
if (validAIMoves.length === 0) return;
// Advanced AI Strategy Implementation
var bestMove = chooseBestAIMove(validAIMoves);
if (bestMove) {
// Check if this is a snake move and handle multi-move sequence
if (bestMove.piece.pieceType === 'snake') {
// Execute the move after a short delay
LK.setTimeout(function () {
movePiece(bestMove.piece, bestMove.toRow, bestMove.toCol);
// If snake has more moves remaining and it's still the AI's turn, continue with AI moves
if (currentSnakePiece && currentSnakePiece.pieceColor === 'black' && snakeMovesRemaining > 0 && currentPlayer === 'black') {
// Delay before next snake move
LK.setTimeout(function () {
makeAIMove(); // Recursive call for next snake move
}, 800);
}
}, 800);
} else {
// Execute regular move after a short delay
LK.setTimeout(function () {
movePiece(bestMove.piece, bestMove.toRow, bestMove.toCol);
}, 800);
}
}
}
function isMoveThreatening(fromRow, fromCol, toRow, toCol, targetRow, targetCol) {
// Simple check if move gets closer to target (used for king protection)
var currentDistance = Math.abs(fromRow - targetRow) + Math.abs(fromCol - targetCol);
var newDistance = Math.abs(toRow - targetRow) + Math.abs(toCol - targetCol);
return newDistance < currentDistance;
}
function chooseBestAIMove(validMoves) {
// Super Grandmaster AI - Perfect calculation depth and evaluation
var bestMove = null;
var bestScore = -100000;
var calculationDepth = 5; // Deep calculation
// Priority 1: Immediate checkmate (highest priority)
var checkmateMove = findCheckmateInOne(validMoves);
if (checkmateMove) {
return checkmateMove;
}
// Priority 2: Prevent opponent checkmate threats
var preventCheckmateMove = preventOpponentCheckmate(validMoves);
if (preventCheckmateMove) {
return preventCheckmateMove;
}
// Priority 3: Emergency king defense when in check
if (isInCheck('black')) {
var defensiveMove = findOptimalDefensiveMove(validMoves);
if (defensiveMove) {
return defensiveMove;
}
}
// Priority 4: Princess elimination for maximum material gain
var princessEliminationMove = findBestPrincessElimination();
if (princessEliminationMove) {
return princessEliminationMove;
}
// Priority 5: Deep tactical combinations
var tacticalMove = findBestTacticalCombination(validMoves);
if (tacticalMove) {
return tacticalMove;
}
// Priority 6: Advanced positional play with perfect evaluation
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var score = evaluateMovePerfectly(move, calculationDepth);
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
}
return bestMove || validMoves[0];
}
function findCheckmateMove(validMoves) {
// Look for moves that can checkmate the opponent
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (simulateMove(move, function () {
return isCheckmate('white');
})) {
return move;
}
}
return null;
}
function findBestCapture(validMoves) {
var bestCapture = null;
var bestValue = 0;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
var captureValue = getPieceValue(targetPiece.pieceType);
// Prioritize king captures highly
if (targetPiece.pieceType === 'king') {
captureValue = 1000;
}
if (captureValue > bestValue) {
bestValue = captureValue;
bestCapture = move;
}
}
}
return bestCapture;
}
function shouldTakeCapture(captureMove) {
// Analyze if capture is safe by looking ahead 2 moves
var isSafe = simulateMove(captureMove, function () {
// Check if the capturing piece can be counter-captured
var capturedPosition = {
row: captureMove.toRow,
col: captureMove.toCol
};
var opponentMoves = getAllValidMoves('white');
for (var i = 0; i < opponentMoves.length; i++) {
var counterMove = opponentMoves[i];
if (counterMove.toRow === capturedPosition.row && counterMove.toCol === capturedPosition.col) {
var counterValue = getPieceValue(counterMove.piece.pieceType);
var captureValue = getPieceValue(getPieceAt(captureMove.toRow, captureMove.toCol).pieceType);
// Only take if gain is worth the risk
return captureValue > counterValue;
}
}
return true; // Safe capture
});
return isSafe;
}
function findTacticalMove(validMoves) {
// Look for moves that create pressure or tactical advantages
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
// Check if move puts opponent in check
if (simulateMove(move, function () {
return isInCheck('white');
})) {
return move;
}
// Check if move creates fork or pin opportunities
if (createsFork(move) || createsPin(move)) {
return move;
}
}
return null;
}
function findWithdrawMove(validMoves) {
// Sometimes withdraw valuable pieces when under threat
var valuablePieces = [];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'black' && (piece.pieceType === 'princess' || piece.pieceType === 'queen' || piece.pieceType === 'snake' || piece.pieceType === 'rook')) {
if (isPieceUnderThreat(piece)) {
valuablePieces.push(piece);
}
}
}
for (var i = 0; i < valuablePieces.length; i++) {
var piece = valuablePieces[i];
for (var j = 0; j < validMoves.length; j++) {
var move = validMoves[j];
if (move.piece === piece && !isPieceUnderThreatAt(piece, move.toRow, move.toCol)) {
// Use knight to create pressure occasionally
if (Math.random() < 0.3 && piece.pieceType === 'knight') {
if (createsPressure(move)) {
return move;
}
}
return move; // Safe withdrawal
}
}
}
return null;
}
function findSafePositionalMove(validMoves) {
// Choose a move that improves position without risk
var safeMoves = [];
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (!isPieceUnderThreatAt(move.piece, move.toRow, move.toCol)) {
safeMoves.push(move);
}
}
if (safeMoves.length > 0) {
return safeMoves[Math.floor(Math.random() * safeMoves.length)];
}
return validMoves[Math.floor(Math.random() * validMoves.length)];
}
function simulateMove(move, testFunction) {
// Temporarily execute move and test condition
var originalRow = move.piece.row;
var originalCol = move.piece.col;
var capturedPiece = getPieceAt(move.toRow, move.toCol);
// Make temporary move
gameBoard[originalRow][originalCol] = null;
gameBoard[move.toRow][move.toCol] = move.piece;
move.piece.row = move.toRow;
move.piece.col = move.toCol;
var result = testFunction();
// Restore position
move.piece.row = originalRow;
move.piece.col = originalCol;
gameBoard[originalRow][originalCol] = move.piece;
gameBoard[move.toRow][move.toCol] = capturedPiece;
return result;
}
function isPieceUnderThreat(piece) {
var whitePieces = pieces.filter(function (p) {
return p.pieceColor === 'white';
});
for (var i = 0; i < whitePieces.length; i++) {
if (whitePieces[i].canMoveTo(piece.row, piece.col)) {
return true;
}
}
return false;
}
function isPieceUnderThreatAt(piece, row, col) {
var whitePieces = pieces.filter(function (p) {
return p.pieceColor === 'white';
});
for (var i = 0; i < whitePieces.length; i++) {
if (whitePieces[i].canMoveTo(row, col)) {
return true;
}
}
return false;
}
function createsFork(move) {
// Check if move attacks two or more enemy pieces
var attackCount = 0;
var whitePieces = pieces.filter(function (p) {
return p.pieceColor === 'white';
});
return simulateMove(move, function () {
for (var i = 0; i < whitePieces.length; i++) {
if (move.piece.canMoveTo(whitePieces[i].row, whitePieces[i].col)) {
attackCount++;
}
}
return attackCount >= 2;
});
}
function createsPin(move) {
// Simplified pin detection - attacks piece that's protecting the king
var whiteKing = findKing('white');
if (!whiteKing) return false;
return simulateMove(move, function () {
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
// Check if removing this piece would expose king
var originalRow = targetPiece.row;
var originalCol = targetPiece.col;
gameBoard[originalRow][originalCol] = null;
var exposesKing = move.piece.canMoveTo(whiteKing.row, whiteKing.col);
gameBoard[originalRow][originalCol] = targetPiece;
return exposesKing;
}
return false;
});
}
function createsPressure(move) {
// Check if move creates pressure on opponent's position
var pressureScore = 0;
var whitePieces = pieces.filter(function (p) {
return p.pieceColor === 'white';
});
return simulateMove(move, function () {
for (var i = 0; i < whitePieces.length; i++) {
var piece = whitePieces[i];
if (move.piece.canMoveTo(piece.row, piece.col)) {
pressureScore += getPieceValue(piece.pieceType);
}
}
return pressureScore > 3; // Creates meaningful pressure
});
}
function findHighValueCapture(validMoves) {
// AI prioritizes capturing high value pieces with any piece
var bestCapture = null;
var bestPriority = 0;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
// Prioritize capturing high value pieces normally
var priority = getPieceValue(targetPiece.pieceType);
if (priority > bestPriority) {
bestPriority = priority;
bestCapture = move;
}
}
}
return bestCapture;
}
function findPoisonOpportunity(validMoves) {
// AI uses snake to move closer to enemy pieces protecting the king to poison them
var whiteKing = findKing('white');
if (!whiteKing) return null;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (move.piece.pieceType === 'snake') {
// Check if this snake move would poison pieces near the white king
var kingProtectors = getKingProtectors('white');
if (kingProtectors.length > 0) {
// Check if moving snake to this position would poison any king protectors
var wouldPoisonProtector = simulateMove(move, function () {
var poisonPositions = [{
row: move.toRow - 1,
col: move.toCol - 1
}, {
row: move.toRow - 1,
col: move.toCol
}, {
row: move.toRow - 1,
col: move.toCol + 1
}, {
row: move.toRow,
col: move.toCol - 1
}, {
row: move.toRow,
col: move.toCol + 1
}, {
row: move.toRow + 1,
col: move.toCol - 1
}, {
row: move.toRow + 1,
col: move.toCol
}, {
row: move.toRow + 1,
col: move.toCol + 1
}];
for (var j = 0; j < poisonPositions.length; j++) {
var pos = poisonPositions[j];
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
var pieceAtPos = getPieceAt(pos.row, pos.col);
if (pieceAtPos && pieceAtPos.pieceColor === 'white') {
for (var k = 0; k < kingProtectors.length; k++) {
if (kingProtectors[k] === pieceAtPos) {
return true;
}
}
}
}
}
return false;
});
if (wouldPoisonProtector) {
return move;
}
}
}
}
return null;
}
function getKingProtectors(color) {
// Find pieces that are protecting the king
var king = findKing(color);
if (!king) return [];
var protectors = [];
var enemyColor = color === 'white' ? 'black' : 'white';
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === color && piece !== king) {
// Check if removing this piece would expose king to attack
var originalRow = piece.row;
var originalCol = piece.col;
gameBoard[originalRow][originalCol] = null;
var exposesKing = false;
for (var j = 0; j < pieces.length; j++) {
var enemyPiece = pieces[j];
if (enemyPiece.pieceColor === enemyColor && enemyPiece.canMoveTo(king.row, king.col)) {
exposesKing = true;
break;
}
}
gameBoard[originalRow][originalCol] = piece;
if (exposesKing) {
protectors.push(piece);
}
}
}
return protectors;
}
function canEscapeCheck(piece, newRow, newCol) {
// Simulate the move to see if it gets the king out of check
var originalRow = piece.row;
var originalCol = piece.col;
var capturedPiece = getPieceAt(newRow, newCol);
// Make temporary move
gameBoard[originalRow][originalCol] = null;
gameBoard[newRow][newCol] = piece;
piece.row = newRow;
piece.col = newCol;
// Check if still in check after move
var stillInCheck = isInCheck(piece.pieceColor);
// Restore original position
piece.row = originalRow;
piece.col = originalCol;
gameBoard[originalRow][originalCol] = piece;
gameBoard[newRow][newCol] = capturedPiece;
return !stillInCheck;
}
function isKingSafeAt(king, newRow, newCol) {
// Check if king would be safe at this position
var originalRow = king.row;
var originalCol = king.col;
var capturedPiece = getPieceAt(newRow, newCol);
// Temporarily move king to test position
gameBoard[originalRow][originalCol] = null;
gameBoard[newRow][newCol] = king;
king.row = newRow;
king.col = newCol;
// Check if king would be in check at new position (including snake threats)
var wouldBeInCheck = isInCheck(king.pieceColor);
// Additional check specifically for snake multi-move threats
if (!wouldBeInCheck) {
wouldBeInCheck = isKingThreatenedBySnake(king, newRow, newCol);
}
// Restore original position
king.row = originalRow;
king.col = originalCol;
gameBoard[originalRow][originalCol] = king;
gameBoard[newRow][newCol] = capturedPiece;
return !wouldBeInCheck;
}
function hasValidEscapeMoves(color) {
// Check if color has any valid moves when in check, considering snake threats
var king = findKing(color);
if (!king) return false;
// Check all possible king moves
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue;
var newRow = king.row + dr;
var newCol = king.col + dc;
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 16) {
var targetPiece = getPieceAt(newRow, newCol);
if (!targetPiece || targetPiece.pieceColor !== color) {
if (isKingSafeAt(king, newRow, newCol)) {
return true; // Found a safe escape square
}
}
}
}
}
// Check if any other piece can block or capture to protect king
var allMoves = getAllValidMoves(color);
return allMoves.length > 0;
}
function findThreateningPiece(king) {
// Find the piece that is threatening the king, including snake multi-move threats
if (!king) return null;
// Check immediate threats first
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor !== king.pieceColor && piece.canMoveTo(king.row, king.col)) {
return piece;
}
}
// Check for snake multi-move threats
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'snake' && piece.pieceColor !== king.pieceColor && !piece.snakeOnCooldown) {
if (canSnakeReachPosition(piece, king.row, king.col)) {
return piece; // Snake can reach king position
}
}
}
return null;
}
function formatTime(milliseconds) {
var totalSeconds = Math.max(0, Math.floor(milliseconds / 1000));
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
return minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
}
function updateTimerDisplay() {
whiteTimerText.setText(formatTime(whiteTimeRemaining));
blackTimerText.setText(formatTime(blackTimeRemaining));
}
function startTimer() {
if (timerInterval) {
LK.clearInterval(timerInterval);
}
currentTurnStartTime = Date.now();
gameStartTime = gameStartTime || currentTurnStartTime;
timerInterval = LK.setInterval(function () {
if (gameState === 'playing' || gameState === 'check') {
var now = Date.now();
var elapsed = now - currentTurnStartTime;
if (currentPlayer === 'white') {
whiteTimeRemaining -= elapsed;
if (whiteTimeRemaining <= 0) {
whiteTimeRemaining = 0;
handleTimeOut('white');
return;
}
} else {
blackTimeRemaining -= elapsed;
if (blackTimeRemaining <= 0) {
blackTimeRemaining = 0;
handleTimeOut('black');
return;
}
}
updateTimerDisplay();
currentTurnStartTime = now;
}
}, 100);
}
function stopTimer() {
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
}
function handleTimeOut(player) {
stopTimer();
var winner = player === 'white' ? 'Black' : 'White';
turnText.setText(winner + ' Wins! ' + (player === 'white' ? 'White' : 'Black') + ' ran out of time!');
gameState = 'gameover';
LK.setTimeout(function () {
restartGame();
}, 2000);
}
function updateLivesDisplay() {
// Update text
whiteLivesText.setText('Your Lives: ' + whiteKingLives);
blackLivesText.setText('Enemy Lives: ' + blackKingLives);
// Update visual hearts with smooth animation
for (var i = 0; i < 4; i++) {
if (i < whiteKingLives) {
tween(whiteLivesHearts[i], {
alpha: 1.0,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
} else {
tween(whiteLivesHearts[i], {
alpha: 0.3,
tint: 0x666666
}, {
duration: 300,
easing: tween.easeOut
});
}
if (i < blackKingLives) {
tween(blackLivesHearts[i], {
alpha: 1.0,
tint: 0xFF0000
}, {
duration: 300,
easing: tween.easeOut
});
} else {
tween(blackLivesHearts[i], {
alpha: 0.3,
tint: 0x666666
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
function updateStaminaDisplay() {
// Update stamina bar visual indicators
for (var i = 0; i < maxStaminaPoints; i++) {
if (i < staminaPoints) {
tween(staminaBars[i], {
alpha: 1.0,
tint: 0xff69b4
}, {
duration: 300,
easing: tween.easeOut
});
} else {
tween(staminaBars[i], {
alpha: 0.3,
tint: 0x666666
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Update AI stamina bar visual indicators
for (var i = 0; i < aiMaxStaminaPoints; i++) {
if (i < aiStaminaPoints) {
tween(aiStaminaBars[i], {
alpha: 1.0,
tint: 0xff69b4
}, {
duration: 300,
easing: tween.easeOut
});
} else {
tween(aiStaminaBars[i], {
alpha: 0.3,
tint: 0x666666
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Update Queen and Snake pink square highlighting
updateQueenSnakePinkSquares();
}
function activateAbility(color, remainingLives) {
var abilityName = '';
var abilityEffect = '';
switch (remainingLives) {
case 3:
abilityName = 'Shield Boost';
abilityEffect = 'All pieces gain temporary protection';
// Flash all pieces of that color green to show protection
flashPiecesOfColor(color, 0x00FF00, 1000);
break;
case 2:
abilityName = 'Speed Rush';
abilityEffect = 'Pieces move faster for next 3 turns';
// Make pieces glow blue to show speed boost
flashPiecesOfColor(color, 0x0099FF, 1500);
break;
case 1:
abilityName = 'Rage Mode';
abilityEffect = 'Double damage on next capture';
// Make pieces glow red to show rage
flashPiecesOfColor(color, 0xFF3333, 2000);
break;
case 0:
abilityName = 'Final Stand';
abilityEffect = 'Last desperate attempt';
break;
}
// Show ability activation message
var message = color.charAt(0).toUpperCase() + color.slice(1) + ' Ability: ' + abilityName;
var abilityText = new Text2(message, {
size: 60,
fill: color === 'white' ? 0xFFFFFF : 0xFF0000
});
abilityText.anchor.set(0.5, 0.5);
abilityText.x = 1024;
abilityText.y = 1200;
game.addChild(abilityText);
// Start with small scale and animate in
abilityText.scaleX = 0.5;
abilityText.scaleY = 0.5;
abilityText.alpha = 0;
// Animate ability text entrance
tween(abilityText, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Hold for a moment then fade out
LK.setTimeout(function () {
tween(abilityText, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0,
y: abilityText.y - 50
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
abilityText.destroy();
}
});
}, 1000);
}
});
}
function flashPiecesOfColor(color, flashColor, duration) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color) {
LK.effects.flashObject(pieces[i], flashColor, duration);
}
}
}
function applyPoisonBehindSnake(snake, oldRow, oldCol) {
// Apply poison to pieces around the snake's previous position (behind it)
var poisonPositions = [{
row: oldRow - 1,
col: oldCol - 1
}, {
row: oldRow - 1,
col: oldCol
}, {
row: oldRow - 1,
col: oldCol + 1
}, {
row: oldRow,
col: oldCol - 1
}, {
row: oldRow,
col: oldCol + 1
}, {
row: oldRow + 1,
col: oldCol - 1
}, {
row: oldRow + 1,
col: oldCol
}, {
row: oldRow + 1,
col: oldCol + 1
}];
for (var i = 0; i < poisonPositions.length; i++) {
var pos = poisonPositions[i];
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
var pieceAtPos = getPieceAt(pos.row, pos.col);
if (pieceAtPos && pieceAtPos.pieceColor !== snake.pieceColor) {
// Kings are immune to poison - skip poisoning kings completely
if (pieceAtPos.pieceType === 'king') {
continue; // Skip poisoning kings
}
// Snake cannot poison the king
if (pieceAtPos.pieceType === 'king') {
continue; // Skip poisoning kings
}
pieceAtPos.applyPoison(3); // Poison for 3 turns
// Flash piece red to show poison application with proper closure
var targetPiece = pieceAtPos; // Capture the piece in closure
tween(targetPiece, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(targetPiece, {
tint: 0xff6666
}, {
duration: 200,
easing: tween.easeOut
});
}
});
}
}
}
}
function restartGame() {
// Stop timer when game ends
stopTimer();
// Determine winner before clearing state
var winner = whiteKingLives <= 0 ? 'Black' : 'White';
// Show win screen with team name
showWinScreen(winner);
}
function showWinScreen(winningTeam) {
// Create win screen overlay
var winScreen = new Container();
game.addChild(winScreen);
// Semi-transparent background
var background = LK.getAsset('darkSquare', {
anchorX: 0,
anchorY: 0,
alpha: 0.8,
scaleX: 20,
scaleY: 25
});
background.x = 0;
background.y = 0;
winScreen.addChild(background);
// Win text
var winText = new Text2(winningTeam + (winningTeam === 'Black' ? ' side won.' : ' Team Wins!'), {
size: 120,
fill: winningTeam === 'White' ? 0xFFFFFF : 0xFF0000
});
winText.anchor.set(0.5, 0.5);
winText.x = 1024;
winText.y = 1366;
winScreen.addChild(winText);
// Animate win text
tween(winText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeOut
});
// Auto-restart after 1 second
LK.setTimeout(function () {
winScreen.destroy();
actualRestartGame();
}, 1000);
}
function shufflePiecesForNewRound(pieceSetup) {
// Create array of non-pawn, non-king pieces for swapping
var swappablePieces = ['summoner', 'knight', 'bishop', 'queen', 'queen', 'knight', 'rook', 'jester', 'knight', 'bishop', 'princess', 'princess', 'snake', 'jester'];
// Create shuffled version of swappable pieces for each side
var shuffledPiecesBlack = swappablePieces.slice(); // Copy array for black
var shuffledPiecesWhite = swappablePieces.slice(); // Copy array for white
// Shuffle black pieces
for (var i = shuffledPiecesBlack.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = shuffledPiecesBlack[i];
shuffledPiecesBlack[i] = shuffledPiecesBlack[j];
shuffledPiecesBlack[j] = temp;
}
// Shuffle white pieces separately
for (var i = shuffledPiecesWhite.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = shuffledPiecesWhite[i];
shuffledPiecesWhite[i] = shuffledPiecesWhite[j];
shuffledPiecesWhite[j] = temp;
}
// Replace pieces in setup while keeping king in center
var blackPieceIndex = 0;
var whitePieceIndex = 0;
// Shuffle black pieces (top row)
for (var col = 0; col < 16; col++) {
if (pieceSetup[0][col] !== 'king' && pieceSetup[0][col] !== 'pawn') {
pieceSetup[0][col] = shuffledPiecesBlack[blackPieceIndex % shuffledPiecesBlack.length];
blackPieceIndex++;
}
}
// Create white setup by shuffling pieces for bottom row (this will be used in piece creation)
var whiteSetup = pieceSetup[0].slice(); // Copy the structure
for (var col = 0; col < 16; col++) {
if (whiteSetup[col] !== 'king' && whiteSetup[col] !== 'pawn') {
whiteSetup[col] = shuffledPiecesWhite[whitePieceIndex % shuffledPiecesWhite.length];
whitePieceIndex++;
}
}
// Store white setup for use in piece creation
pieceSetup.whiteSetup = whiteSetup;
return pieceSetup;
}
function randomlySwapNonPawnPieces(pieceSetup) {
// Keep the original function for backward compatibility
return shufflePiecesForNewRound(pieceSetup);
}
function actualRestartGame() {
// Clear all pieces
for (var i = 0; i < pieces.length; i++) {
pieces[i].destroy();
}
pieces = [];
// Clear highlights
clearHighlights();
// Clear pink squares
for (var i = 0; i < queenSnakePinkSquares.length; i++) {
if (!queenSnakePinkSquares[i].destroyed) {
queenSnakePinkSquares[i].destroy();
}
}
queenSnakePinkSquares = [];
// Clear king squares
if (kingSquare) {
kingSquare.destroy();
kingSquare = null;
}
if (blackKingSquare) {
blackKingSquare.destroy();
blackKingSquare = null;
}
// Reset game state
currentPlayer = 'white';
selectedPiece = null;
validMoves = [];
dragNode = null;
gameState = 'playing';
whiteKingLives = 4;
blackKingLives = 4;
currentSnakePiece = null;
snakeMovesRemaining = 0;
activeSnakePiece = null; // Reset active snake
// Reset stamina system
staminaPoints = 1;
aiStaminaPoints = 1;
updateStaminaDisplay();
// Reset timer variables
whiteTimeRemaining = 60000;
blackTimeRemaining = 60000;
gameStartTime = null;
currentTurnStartTime = null;
// Snake cooldowns will be reset when new pieces are created - no need to access destroyed pieces
updateLivesDisplay();
turnText.setText('Your Turn');
// Clear board
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
gameBoard[row][col] = null;
}
}
// Create base piece setup and shuffle non-pawn pieces for new round
var basePieceSetup = [['summoner', 'knight', 'bishop', 'queen', 'king', 'queen', 'bishop', 'knight', 'rook', 'jester', 'knight', 'bishop', 'princess', 'princess', 'snake', 'jester'], ['pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn']];
var pieceSetup = shufflePiecesForNewRound(basePieceSetup);
// Create black pieces
for (var row = 0; row < 2; row++) {
for (var col = 0; col < 16; col++) {
var piece = new ChessPiece(pieceSetup[row][col], 'black', row, col);
pieces.push(piece);
gameBoard[row][col] = piece;
game.addChild(piece);
piece.updatePosition();
}
}
// Create white pieces
for (var row = 6; row < 8; row++) {
for (var col = 0; col < 16; col++) {
var pieceType = row === 6 ? 'pawn' : pieceSetup.whiteSetup[col];
var piece = new ChessPiece(pieceType, 'white', row, col);
pieces.push(piece);
gameBoard[row][col] = piece;
game.addChild(piece);
piece.updatePosition();
}
}
// Initialize king square after restart
updateKingSquare();
// Restart timer
updateTimerDisplay();
startTimer();
}
game.down = function (x, y, obj) {
if (gameState !== 'playing' && gameState !== 'check') return;
if (currentPlayer !== 'white') return; // Only allow white player to move pieces
var square = getSquareFromPosition(x, y);
if (!square) return;
var clickedPiece = getPieceAt(square.row, square.col);
// If we have a selected piece, try to move it
if (selectedPiece) {
// Check if clicked on valid move
var isValidMove = false;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === square.row && validMoves[i].col === square.col) {
isValidMove = true;
break;
}
}
if (isValidMove) {
movePiece(selectedPiece, square.row, square.col);
clearHighlights();
selectedPiece = null;
return;
} else {
// Invalid move or clicking elsewhere - clear selection
clearHighlights();
selectedPiece = null;
}
}
// Handle princess elimination mode
if (selectedPiece && selectedPiece.pieceType === 'princess' && selectedPiece.princessEliminationMode) {
if (clickedPiece && clickedPiece.pieceColor !== selectedPiece.pieceColor) {
// Check if target is within princess's elimination range (2 squares in any direction)
var rowDiff = Math.abs(clickedPiece.row - selectedPiece.row);
var colDiff = Math.abs(clickedPiece.col - selectedPiece.col);
var maxRange = Math.max(rowDiff, colDiff);
if (maxRange <= 2 && selectedPiece.princessSelectedTargets.indexOf(clickedPiece) === -1) {
selectedPiece.princessSelectedTargets.push(clickedPiece);
selectedPiece.princessTargetsSelected++;
highlightSquare(clickedPiece.row, clickedPiece.col, 'blue');
turnText.setText('Princess selected target ' + selectedPiece.princessTargetsSelected + '/2');
if (selectedPiece.princessTargetsSelected === 2) {
// Execute elimination
for (var i = 0; i < selectedPiece.princessSelectedTargets.length; i++) {
var target = selectedPiece.princessSelectedTargets[i];
for (var j = 0; j < pieces.length; j++) {
if (pieces[j] === target) {
gameBoard[target.row][target.col] = null;
target.destroy();
pieces.splice(j, 1);
break;
}
}
}
LK.getSound('capture').play();
// Reset princess state and switch turns
selectedPiece.princessEliminationMode = false;
selectedPiece.princessTargetsSelected = 0;
selectedPiece.princessSelectedTargets = [];
clearHighlights();
selectedPiece = null;
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
turnText.setText(currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1) + '\'s Turn');
if (currentPlayer === 'black') {
makeAIMove();
}
return;
}
}
}
return;
}
// If there's an active snake continuing its moves, only allow that snake to be selected
if (activeSnakePiece && activeSnakePiece.pieceColor === 'white' && snakeMovesRemaining > 0) {
if (clickedPiece === activeSnakePiece) {
selectedPiece = activeSnakePiece;
dragNode = activeSnakePiece;
showValidMoves(activeSnakePiece);
}
return;
}
// Normal piece selection - only allow white pieces to be selected
if (clickedPiece && clickedPiece.pieceColor === 'white') {
// Add selection animation
tween(clickedPiece, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(clickedPiece, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Check for princess double-click to enter elimination mode
if (clickedPiece.pieceType === 'princess' && selectedPiece === clickedPiece) {
clickedPiece.princessEliminationMode = true;
clickedPiece.princessTargetsSelected = 0;
clickedPiece.princessSelectedTargets = [];
clearHighlights();
highlightSquare(clickedPiece.row, clickedPiece.col, 'blue');
// Show elimination range (2 squares in all directions)
for (var dr = -2; dr <= 2; dr++) {
for (var dc = -2; dc <= 2; dc++) {
if (dr === 0 && dc === 0) continue; // Skip princess's own square
var targetRow = clickedPiece.row + dr;
var targetCol = clickedPiece.col + dc;
if (targetRow >= 0 && targetRow < 8 && targetCol >= 0 && targetCol < 16) {
var targetPiece = getPieceAt(targetRow, targetCol);
if (targetPiece && targetPiece.pieceColor !== clickedPiece.pieceColor) {
highlightSquare(targetRow, targetCol, 'green'); // Show valid targets in green
} else if (!targetPiece) {
highlightSquare(targetRow, targetCol, 'yellow'); // Show empty squares in elimination range
}
}
}
}
turnText.setText('Princess Elimination Mode: Select 2 enemies within 2 squares');
return;
}
// Select the piece and show valid moves
selectedPiece = clickedPiece;
dragNode = clickedPiece;
showValidMoves(clickedPiece);
}
};
game.move = function (x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
};
game.up = function (x, y, obj) {
if (dragNode && selectedPiece) {
var square = getSquareFromPosition(x, y);
if (square) {
var isValidMove = false;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === square.row && validMoves[i].col === square.col) {
isValidMove = true;
break;
}
}
if (isValidMove) {
movePiece(selectedPiece, square.row, square.col);
clearHighlights();
selectedPiece = null;
} else {
selectedPiece.updatePosition(true);
}
} else {
selectedPiece.updatePosition(true);
}
}
dragNode = null;
};
game.update = function () {
// Game logic runs automatically through event handlers
};
function findAggressiveSnakeMove(validMoves) {
// AI uses snake aggressively like a queen for attacks and board control
var bestSnakeMove = null;
var bestValue = 0;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (move.piece.pieceType === 'snake') {
var moveValue = 0;
// Prioritize captures with snake
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
moveValue += getPieceValue(targetPiece.pieceType) * 2; // Double weight for snake captures
}
// Prioritize moves that put enemy king in check
if (simulateMove(move, function () {
return isInCheck('white');
})) {
moveValue += 15; // High priority for check moves
}
// Prioritize moves that control center squares
var centerDistance = Math.abs(move.toRow - 3.5) + Math.abs(move.toCol - 7.5);
moveValue += (16 - centerDistance) * 0.5; // Favor central positions
// Prioritize moves that attack multiple pieces
var attackCount = 0;
var whitePieces = pieces.filter(function (p) {
return p.pieceColor === 'white';
});
simulateMove(move, function () {
for (var j = 0; j < whitePieces.length; j++) {
if (move.piece.canMoveTo(whitePieces[j].row, whitePieces[j].col)) {
attackCount++;
}
}
return false;
});
moveValue += attackCount * 3; // Bonus for forking
// Avoid moves that put snake in immediate danger unless high value
if (!isPieceUnderThreatAt(move.piece, move.toRow, move.toCol) || moveValue > 10) {
if (moveValue > bestValue) {
bestValue = moveValue;
bestSnakeMove = move;
}
}
}
}
return bestSnakeMove;
}
function findSnakeTacticalMove(validMoves) {
// Find tactical moves specifically for snake pieces - aggressive positioning
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (move.piece.pieceType === 'snake') {
// Snake moves to attack enemy pieces near their king
var whiteKing = findKing('white');
if (whiteKing) {
var distanceToKing = Math.abs(move.toRow - whiteKing.row) + Math.abs(move.toCol - whiteKing.col);
// Prioritize moves that get snake closer to enemy king area
if (distanceToKing <= 3) {
var currentDistance = Math.abs(move.piece.row - whiteKing.row) + Math.abs(move.piece.col - whiteKing.col);
if (distanceToKing < currentDistance) {
return move; // Move closer to king
}
}
}
// Snake moves to create pressure on enemy back rank
if (move.toRow <= 2) {
// Near enemy back rank
return move;
}
// Snake moves to control key squares
var keySquares = [{
row: 3,
col: 7
}, {
row: 3,
col: 8
}, {
row: 4,
col: 7
}, {
row: 4,
col: 8
}]; // Center control
for (var j = 0; j < keySquares.length; j++) {
if (move.toRow === keySquares[j].row && move.toCol === keySquares[j].col) {
return move;
}
}
}
}
return null;
}
function findDefensivePrincessMove(validMoves) {
// AI uses princess and queen more timidly for solid compression and defensive checkmate setups
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (move.piece.pieceType === 'princess' || move.piece.pieceType === 'queen') {
// Queen stays back and supports other pieces
var backRankSafety = move.toRow <= 2; // Stay in back ranks
if (backRankSafety) {
// Quenn supports checkmate patterns by controlling escape squares
var whiteKing = findKing('white');
if (whiteKing) {
// Check if queen move controls king's escape squares
var controlsEscape = false;
var escapeSquares = [{
row: whiteKing.row - 1,
col: whiteKing.col - 1
}, {
row: whiteKing.row - 1,
col: whiteKing.col
}, {
row: whiteKing.row - 1,
col: whiteKing.col + 1
}, {
row: whiteKing.row,
col: whiteKing.col - 1
}, {
row: whiteKing.row,
col: whiteKing.col + 1
}, {
row: whiteKing.row + 1,
col: whiteKing.col - 1
}, {
row: whiteKing.row + 1,
col: whiteKing.col
}, {
row: whiteKing.row + 1,
col: whiteKing.col + 1
}];
simulateMove(move, function () {
for (var j = 0; j < escapeSquares.length; j++) {
var escSquare = escapeSquares[j];
if (escSquare.row >= 0 && escSquare.row < 8 && escSquare.col >= 0 && escSquare.col < 16) {
if (move.piece.canMoveTo(escSquare.row, escSquare.col)) {
controlsEscape = true;
break;
}
}
}
return false;
});
if (controlsEscape) {
return move; // Queen controls escape squares for checkmate
}
}
// Queen defends other pieces
var defendsAllies = false;
simulateMove(move, function () {
for (var j = 0; j < pieces.length; j++) {
var ally = pieces[j];
if (ally.pieceColor === 'black' && ally !== move.piece) {
if (isPieceUnderThreat(ally)) {
// Check if queen can defend this piece
if (move.piece.canMoveTo(ally.row, ally.col)) {
defendsAllies = true;
break;
}
}
}
}
return false;
});
if (defendsAllies) {
return move; // Queen defends allies
}
}
// Only make aggressive queen moves if very safe and high value
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
var captureValue = getPieceValue(targetPiece.pieceType);
if (captureValue >= 5 && !isPieceUnderThreatAt(move.piece, move.toRow, move.toCol)) {
return move; // Safe high-value capture only
}
}
}
}
return null;
}
function isKingThreatenedBySnake(king, kingRow, kingCol) {
// Check if any enemy snake can reach the king position through its multi-move capability
var enemyColor = king.pieceColor === 'white' ? 'black' : 'white';
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'snake' && piece.pieceColor === enemyColor && !piece.snakeOnCooldown) {
// Check if snake can reach king in 1-3 moves
if (canSnakeReachPosition(piece, kingRow, kingCol)) {
return true;
}
}
}
return false;
}
function canSnakeReachPosition(snake, targetRow, targetCol) {
// Check if snake can reach target position within its 3-move sequence
// This simulates the snake's complex movement pattern
var visited = {};
function canReachRecursive(currentRow, currentCol, movesLeft, moveType) {
if (movesLeft === 0) return false;
if (currentRow === targetRow && currentCol === targetCol) return true;
var key = currentRow + ',' + currentCol + ',' + movesLeft + ',' + moveType;
if (visited[key]) return false;
visited[key] = true;
// Generate possible moves based on snake move type
var possibleMoves = [];
if (moveType === 0) {
// First move: straight lines only
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue;
// Must be straight line movement
if (dr === 0 || dc === 0 || Math.abs(dr) === Math.abs(dc)) {
var newRow = currentRow + dr;
var newCol = currentCol + dc;
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 16) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== snake.pieceColor) {
possibleMoves.push({
row: newRow,
col: newCol,
nextMoveType: 1
});
}
}
}
}
}
} else if (moveType === 1) {
// Second move: diagonal only
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
if (Math.abs(dr) === Math.abs(dc) && Math.abs(dr) === 1) {
var newRow = currentRow + dr;
var newCol = currentCol + dc;
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 16) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== snake.pieceColor) {
possibleMoves.push({
row: newRow,
col: newCol,
nextMoveType: 2
});
}
}
}
}
}
} else if (moveType === 2) {
// Third move: any direction
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue;
var newRow = currentRow + dr;
var newCol = currentCol + dc;
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 16) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== snake.pieceColor) {
possibleMoves.push({
row: newRow,
col: newCol,
nextMoveType: 3
});
}
}
}
}
}
// Try each possible move
for (var j = 0; j < possibleMoves.length; j++) {
var move = possibleMoves[j];
if (canReachRecursive(move.row, move.col, movesLeft - 1, move.nextMoveType)) {
return true;
}
}
return false;
}
// Start with snake's current position and first move type
return canReachRecursive(snake.row, snake.col, snake.maxSnakeMoves, 0);
}
function evaluateMovePerfectly(move, depth) {
// Perfect evaluation with grandmaster-level understanding
var score = 0;
var piece = move.piece;
var targetPiece = getPieceAt(move.toRow, move.toCol);
// 1. Checkmate detection (perfect priority)
if (simulateMove(move, function () {
return isCheckmate('white');
})) {
return 50000; // Mate is always best
}
// 2. Prevent our own checkmate (critical)
var opponentThreats = getAllValidMoves('white');
var preventsCheckmate = false;
for (var i = 0; i < opponentThreats.length; i++) {
if (simulateMove(move, function () {
return !simulateOpponentMove(opponentThreats[i], function () {
return isCheckmate('black');
});
})) {
preventsCheckmate = true;
break;
}
}
if (preventsCheckmate) score += 10000;
// 3. Perfect material evaluation
if (targetPiece && targetPiece.pieceColor === 'white') {
var captureValue = getPieceValue(targetPiece.pieceType);
var exchangeValue = calculatePerfectExchange(move, depth);
score += exchangeValue * 100;
// Grandmaster piece priorities
if (targetPiece.pieceType === 'queen') score += 900;
if (targetPiece.pieceType === 'princess') score += 800;
if (targetPiece.pieceType === 'snake') score += 600;
if (targetPiece.pieceType === 'rook') score += 500;
}
// 4. Perfect tactical pattern recognition
score += evaluatePerfectTactics(move, depth);
// 5. Strategic piece coordination
score += evaluatePerfectStrategy(move);
// 6. King attack precision
score += evaluatePerfectKingAttack(move);
// 7. Basic positional evaluation
score += evaluatePositionalFactors(move);
return score;
}
function calculatePerfectCoordination(move) {
// Calculate how well pieces coordinate after this move
var coordinationScore = 0;
return simulateMove(move, function () {
// Count pieces that support each other
var supportingPairs = 0;
var totalSupport = 0;
for (var i = 0; i < pieces.length; i++) {
var piece1 = pieces[i];
if (piece1.pieceColor === 'black') {
var supportCount = 0;
for (var j = 0; j < pieces.length; j++) {
var piece2 = pieces[j];
if (piece2.pieceColor === 'black' && piece1 !== piece2) {
// Check if piece2 defends piece1
if (piece2.canMoveTo(piece1.row, piece1.col)) {
supportCount++;
totalSupport++;
}
}
}
if (supportCount > 0) {
supportingPairs++;
}
}
}
// Calculate coordination bonuses
coordinationScore += supportingPairs * 3; // Bonus for each supported piece
coordinationScore += totalSupport * 2; // Bonus for total defensive connections
// Special coordination patterns
// Queen and rook coordination
var blackQueens = pieces.filter(function (p) {
return p.pieceColor === 'black' && p.pieceType === 'queen';
});
var blackRooks = pieces.filter(function (p) {
return p.pieceColor === 'black' && p.pieceType === 'rook';
});
for (var i = 0; i < blackQueens.length; i++) {
for (var j = 0; j < blackRooks.length; j++) {
var rowDiff = Math.abs(blackQueens[i].row - blackRooks[j].row);
var colDiff = Math.abs(blackQueens[i].col - blackRooks[j].col);
if (rowDiff === 0 || colDiff === 0) {
coordinationScore += 5; // Same rank/file coordination
}
}
}
// Bishop pair coordination
var blackBishops = pieces.filter(function (p) {
return p.pieceColor === 'black' && p.pieceType === 'bishop';
});
if (blackBishops.length >= 2) {
coordinationScore += 8; // Bishop pair bonus
}
return coordinationScore;
});
}
function calculatePerfectExchange(move, depth) {
// Calculate exact exchange value with perfect accuracy
var ourPiece = move.piece;
var enemyPiece = getPieceAt(move.toRow, move.toCol);
if (!enemyPiece) return 0;
var exchangeSequence = [];
var currentPos = {
row: move.toRow,
col: move.toCol
};
// Calculate all pieces that can recapture
var recapturers = [];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'white' && piece.canMoveTo(currentPos.row, currentPos.col)) {
recapturers.push({
piece: piece,
value: getPieceValue(piece.pieceType)
});
}
}
// Sort by piece value (sacrifice lowest first)
recapturers.sort(function (a, b) {
return a.value - b.value;
});
var balance = getPieceValue(enemyPiece.pieceType);
var attackerValue = getPieceValue(ourPiece.pieceType);
// Perfect exchange calculation
for (var i = 0; i < Math.min(recapturers.length, depth); i++) {
if (i % 2 === 0) {
balance -= attackerValue;
} else {
balance += recapturers[Math.floor(i / 2)].value;
}
attackerValue = i < recapturers.length ? recapturers[i].value : 0;
}
return balance;
}
function evaluatePerfectTactics(move, depth) {
var score = 0;
// Perfect fork detection
var forkValue = calculatePerfectFork(move);
score += forkValue * 50;
// Perfect pin detection
if (createsPerfectPin(move)) score += 200;
// Perfect discovered attack
if (createsPerfectDiscovery(move)) score += 250;
// Perfect deflection
if (createsPerfectDeflection(move)) score += 180;
// Perfect skewer
if (createsPerfectSkewer(move)) score += 220;
return score;
}
function evaluatePerfectStrategy(move) {
var score = 0;
// Perfect piece coordination
var coordinationValue = calculatePerfectCoordination(move);
score += coordinationValue * 15;
// Basic piece activity
score += evaluatePositionalFactors(move) * 0.5;
// Basic tactical patterns
score += evaluateTacticalPatterns(move) * 0.8;
return score;
}
function evaluatePerfectKingAttack(move) {
var score = 0;
var enemyKing = findKing('white');
if (!enemyKing) return 0;
return simulateMove(move, function () {
var attackingPieces = 0;
var controlledSquares = 0;
// Count pieces attacking king area
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'black') {
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
var kingArea = {
row: enemyKing.row + dr,
col: enemyKing.col + dc
};
if (kingArea.row >= 0 && kingArea.row < 8 && kingArea.col >= 0 && kingArea.col < 16) {
if (piece.canMoveTo(kingArea.row, kingArea.col)) {
controlledSquares++;
if (dr === 0 && dc === 0) attackingPieces++;
}
}
}
}
}
}
return attackingPieces * 30 + controlledSquares * 10;
});
}
function countThreatsToKing(color) {
var king = findKing(color);
if (!king) return 0;
var threats = 0;
var enemyColor = color === 'white' ? 'black' : 'white';
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === enemyColor && piece.canMoveTo(king.row, king.col)) {
threats++;
}
}
return threats;
}
function calculateExchangeValue(move) {
var attackerValue = getPieceValue(move.piece.pieceType);
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (!targetPiece) return 0;
var defenderValue = getPieceValue(targetPiece.pieceType);
// Simulate the capture and see if opponent can recapture
return simulateMove(move, function () {
var opponentMoves = getAllValidMoves('white');
var canRecapture = false;
var recaptureValue = 0;
for (var i = 0; i < opponentMoves.length; i++) {
var opMove = opponentMoves[i];
if (opMove.toRow === move.toRow && opMove.toCol === move.toCol) {
canRecapture = true;
recaptureValue = Math.min(recaptureValue || attackerValue, getPieceValue(opMove.piece.pieceType));
}
}
if (canRecapture) {
return defenderValue - recaptureValue;
} else {
return defenderValue;
}
});
}
function evaluateTacticalPatterns(move) {
var score = 0;
// Check gives reduced bonus (more defensive approach)
if (simulateMove(move, function () {
return isInCheck('white');
})) {
score += 15; // Reduced from 30
}
// Fork pattern - attacking multiple pieces (less aggressive)
var forkTargets = 0;
simulateMove(move, function () {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'white' && move.piece.canMoveTo(piece.row, piece.col)) {
forkTargets++;
}
}
return false;
});
if (forkTargets >= 2) {
score += 20; // Reduced from 40
} else if (forkTargets >= 1) {
score += 5; // Reduced from 10
}
// Pin detection (reduced)
if (createsPinPattern(move)) {
score += 12; // Reduced from 25
}
// Discovered attack (reduced)
if (createsDiscoveredAttack(move)) {
score += 17; // Reduced from 35
}
return score;
}
function evaluatePieceSpecificMove(move) {
var score = 0;
var piece = move.piece;
switch (piece.pieceType) {
case 'snake':
// Snake special abilities
score += evaluateSnakeMove(move);
break;
case 'princess':
// Princess elimination potential
score += evaluatePrincessMove(move);
break;
case 'summoner':
// Summoner positioning and summoning potential
score += evaluateSummonerMove(move);
break;
case 'queen':
// Queen positioning and power
score += evaluateQueenMove(move);
break;
case 'knight':
// Knight outpost evaluation
score += evaluateKnightMove(move);
break;
case 'rook':
// Rook file and rank control
score += evaluateRookMove(move);
break;
case 'bishop':
// Bishop diagonal control
score += evaluateBishopMove(move);
break;
case 'pawn':
// Pawn advancement and promotion
score += evaluatePawnMove(move);
break;
}
return score;
}
function evaluateSnakeMove(move) {
var score = 0;
// Reduced poison aggression - only target when safe
var poisonTargets = 0;
simulateMove(move, function () {
var poisonPositions = [{
row: move.toRow - 1,
col: move.toCol - 1
}, {
row: move.toRow - 1,
col: move.toCol
}, {
row: move.toRow - 1,
col: move.toCol + 1
}, {
row: move.toRow,
col: move.toCol - 1
}, {
row: move.toRow,
col: move.toCol + 1
}, {
row: move.toRow + 1,
col: move.toCol - 1
}, {
row: move.toRow + 1,
col: move.toCol
}, {
row: move.toRow + 1,
col: move.toCol + 1
}];
for (var i = 0; i < poisonPositions.length; i++) {
var pos = poisonPositions[i];
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
var targetPiece = getPieceAt(pos.row, pos.col);
if (targetPiece && targetPiece.pieceColor === 'white' && targetPiece.pieceType !== 'king' && targetPiece.pieceType !== 'queen') {
poisonTargets++;
score += getPieceValue(targetPiece.pieceType) * 2; // Reduced from 5
}
}
}
return false;
});
// Reduced multi-move tactical advantage
if (move.piece.snakeMovesThisTurn === 0) {
score += 8; // Reduced from 15
}
// Favor defensive positioning
var distanceFromOwnKing = Math.abs(move.toRow - findKing('black').row) + Math.abs(move.toCol - findKing('black').col);
if (distanceFromOwnKing <= 4) {
score += 10; // Bonus for staying near own king
}
return score;
}
function evaluateSummonerMove(move) {
var score = 0;
// Bonus for positioning summoner to summon in useful positions
if (move.piece.summonerMoveCount >= 2) {
// About to summon, evaluate summoning positions
var summonPositions = [{
row: move.toRow - 1,
col: move.toCol
},
// up
{
row: move.toRow,
col: move.toCol + 1
},
// right
{
row: move.toRow + 1,
col: move.toCol
},
// down
{
row: move.toRow,
col: move.toCol - 1
},
// left
{
row: move.toRow - 1,
col: move.toCol + 1
},
// up-right
{
row: move.toRow + 1,
col: move.toCol + 1
},
// down-right
{
row: move.toRow + 1,
col: move.toCol - 1
},
// down-left
{
row: move.toRow - 1,
col: move.toCol - 1
} // up-left
];
var goodSummonSpots = 0;
for (var i = 0; i < summonPositions.length && goodSummonSpots < 2; i++) {
var pos = summonPositions[i];
if (pos.row >= 0 && pos.row < 8 && pos.col >= 0 && pos.col < 16) {
if (!getPieceAt(pos.row, pos.col)) {
goodSummonSpots++;
// Bonus for summoning in aggressive positions
if (move.piece.pieceColor === 'black' && pos.row >= 4 || move.piece.pieceColor === 'white' && pos.row <= 3) {
score += 15;
} else {
score += 8;
}
}
}
}
if (goodSummonSpots >= 2) {
score += 25; // Bonus for full summoning potential
}
}
// Strategic positioning like princess
var centralControl = 0;
var centerSquares = [{
row: 3,
col: 7
}, {
row: 3,
col: 8
}, {
row: 4,
col: 7
}, {
row: 4,
col: 8
}];
for (var i = 0; i < centerSquares.length; i++) {
var square = centerSquares[i];
if (Math.abs(move.toRow - square.row) <= 2 && Math.abs(move.toCol - square.col) <= 2) {
centralControl++;
}
}
score += centralControl * 3;
return score;
}
;
function evaluatePrincessMove(move) {
var score = 0;
// Evaluate elimination potential within 2 squares
var enemiesInRange = [];
for (var dr = -2; dr <= 2; dr++) {
for (var dc = -2; dc <= 2; dc++) {
if (dr === 0 && dc === 0) continue;
var checkRow = move.toRow + dr;
var checkCol = move.toCol + dc;
if (checkRow >= 0 && checkRow < 8 && checkCol >= 0 && checkCol < 16) {
var piece = getPieceAt(checkRow, checkCol);
if (piece && piece.pieceColor === 'white') {
enemiesInRange.push(piece);
score += getPieceValue(piece.pieceType) * 2;
}
}
}
}
// Bonus for positioning where elimination can capture 2+ high-value pieces
if (enemiesInRange.length >= 2) {
// Sort enemies by value and consider best 2
enemiesInRange.sort(function (a, b) {
return getPieceValue(b.pieceType) - getPieceValue(a.pieceType);
});
var topTwoValue = getPieceValue(enemiesInRange[0].pieceType) + getPieceValue(enemiesInRange[1].pieceType);
score += topTwoValue * 5; // High bonus for double elimination potential
}
return score;
}
function evaluateQueenMove(move) {
var score = 0;
// Queens should control center and support attacks
var centralControl = 0;
var centerSquares = [{
row: 3,
col: 7
}, {
row: 3,
col: 8
}, {
row: 4,
col: 7
}, {
row: 4,
col: 8
}];
simulateMove(move, function () {
for (var i = 0; i < centerSquares.length; i++) {
var square = centerSquares[i];
if (move.piece.canMoveTo(square.row, square.col)) {
centralControl++;
}
}
return false;
});
score += centralControl * 5;
// Bonus for long-range queen moves
var rowDiff = Math.abs(move.toRow - move.piece.row);
var colDiff = Math.abs(move.toCol - move.piece.col);
var moveDistance = Math.max(rowDiff, colDiff);
if (moveDistance > 2) {
score += 5; // Bonus for using queen's long-range capability
}
// Support other pieces
var supportedAllies = countSupportedAllies(move);
score += supportedAllies * 8;
return score;
}
function evaluatePositionalFactors(move) {
var score = 0;
// Center control
var centerDistance = Math.abs(move.toRow - 3.5) + Math.abs(move.toCol - 7.5);
score += (16 - centerDistance) * 2;
// Piece mobility after move
var mobilityAfter = simulateMove(move, function () {
var validMovesAfter = 0;
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 16; col++) {
if (move.piece.canMoveTo(row, col)) {
validMovesAfter++;
}
}
}
return validMovesAfter;
});
score += mobilityAfter * 1;
// Safety evaluation
if (isPieceUnderThreatAt(move.piece, move.toRow, move.toCol)) {
score -= getPieceValue(move.piece.pieceType) * 3;
}
return score;
}
function evaluateKingAttack(move) {
var score = 0;
var whiteKing = findKing('white');
if (!whiteKing) return 0;
// Reduced emphasis on approaching enemy king
var distanceToKing = Math.abs(move.toRow - whiteKing.row) + Math.abs(move.toCol - whiteKing.col);
var currentDistance = Math.abs(move.piece.row - whiteKing.row) + Math.abs(move.piece.col - whiteKing.col);
if (distanceToKing < currentDistance) {
score += (currentDistance - distanceToKing) * 1; // Reduced from 3
}
// Reduced control emphasis around enemy king
var controlledKingSquares = 0;
simulateMove(move, function () {
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
var kingAdjacentRow = whiteKing.row + dr;
var kingAdjacentCol = whiteKing.col + dc;
if (kingAdjacentRow >= 0 && kingAdjacentRow < 8 && kingAdjacentCol >= 0 && kingAdjacentCol < 16) {
if (move.piece.canMoveTo(kingAdjacentRow, kingAdjacentCol)) {
controlledKingSquares++;
}
}
}
}
return false;
});
score += controlledKingSquares * 3; // Reduced from 8
return score;
}
function evaluatePieceCoordination(move) {
var score = 0;
// Count how many allied pieces this move supports
var supportedAllies = countSupportedAllies(move);
score += supportedAllies * 5;
// Count how many allied pieces support this piece at new position
var supportingAllies = countSupportingAllies(move);
score += supportingAllies * 3;
return score;
}
function countSupportedAllies(move) {
var supported = 0;
return simulateMove(move, function () {
for (var i = 0; i < pieces.length; i++) {
var ally = pieces[i];
if (ally.pieceColor === 'black' && ally !== move.piece) {
if (move.piece.canMoveTo(ally.row, ally.col)) {
supported++;
}
}
}
return supported;
});
}
function countSupportingAllies(move) {
var supporting = 0;
return simulateMove(move, function () {
for (var i = 0; i < pieces.length; i++) {
var ally = pieces[i];
if (ally.pieceColor === 'black' && ally !== move.piece) {
if (ally.canMoveTo(move.toRow, move.toCol)) {
supporting++;
}
}
}
return supporting;
});
}
function createsPinPattern(move) {
var whiteKing = findKing('white');
if (!whiteKing) return false;
return simulateMove(move, function () {
// Check if this move creates a pin on enemy pieces
for (var i = 0; i < pieces.length; i++) {
var enemyPiece = pieces[i];
if (enemyPiece.pieceColor === 'white' && enemyPiece !== whiteKing) {
// Temporarily remove the piece and see if king becomes attackable
var originalRow = enemyPiece.row;
var originalCol = enemyPiece.col;
gameBoard[originalRow][originalCol] = null;
var pinExists = move.piece.canMoveTo(whiteKing.row, whiteKing.col);
gameBoard[originalRow][originalCol] = enemyPiece;
if (pinExists) {
return true;
}
}
}
return false;
});
}
function createsDiscoveredAttack(move) {
// Check if moving this piece uncovers an attack from another piece
var originalRow = move.piece.row;
var originalCol = move.piece.col;
// Temporarily move piece and check if any ally can now attack
return simulateMove(move, function () {
for (var i = 0; i < pieces.length; i++) {
var ally = pieces[i];
if (ally.pieceColor === 'black' && ally !== move.piece) {
for (var j = 0; j < pieces.length; j++) {
var enemy = pieces[j];
if (enemy.pieceColor === 'white') {
// Check if ally can now attack enemy after piece moved
var couldAttackBefore = false;
// Temporarily put piece back at original position
gameBoard[move.toRow][move.toCol] = null;
gameBoard[originalRow][originalCol] = move.piece;
move.piece.row = originalRow;
move.piece.col = originalCol;
couldAttackBefore = ally.canMoveTo(enemy.row, enemy.col);
// Put piece back at new position
gameBoard[originalRow][originalCol] = null;
gameBoard[move.toRow][move.toCol] = move.piece;
move.piece.row = move.toRow;
move.piece.col = move.toCol;
var canAttackAfter = ally.canMoveTo(enemy.row, enemy.col);
if (!couldAttackBefore && canAttackAfter) {
return true;
}
}
}
}
}
return false;
});
}
function findBestPrincessElimination() {
// Find AI princess pieces that can use elimination ability
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceType === 'princess' && piece.pieceColor === 'black') {
// Find enemies within 2 squares of princess
var targetsInRange = [];
for (var dr = -2; dr <= 2; dr++) {
for (var dc = -2; dc <= 2; dc++) {
if (dr === 0 && dc === 0) continue;
var checkRow = piece.row + dr;
var checkCol = piece.col + dc;
if (checkRow >= 0 && checkRow < 8 && checkCol >= 0 && checkCol < 16) {
var targetPiece = getPieceAt(checkRow, checkCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
targetsInRange.push(targetPiece);
}
}
}
}
// If we can eliminate 2+ pieces, prioritize highest value targets
if (targetsInRange.length >= 2) {
// Sort by piece value, highest first
targetsInRange.sort(function (a, b) {
return getPieceValue(b.pieceType) - getPieceValue(a.pieceType);
});
// Check if the top 2 targets are worth eliminating
var topTwoValue = getPieceValue(targetsInRange[0].pieceType) + getPieceValue(targetsInRange[1].pieceType);
if (topTwoValue >= 6) {
// Worth at least 6 points total
// Execute princess elimination
LK.setTimeout(function () {
// Remove the two highest value targets
for (var j = 0; j < 2; j++) {
var target = targetsInRange[j];
for (var k = 0; k < pieces.length; k++) {
if (pieces[k] === target) {
gameBoard[target.row][target.col] = null;
target.destroy();
pieces.splice(k, 1);
break;
}
}
}
LK.getSound('capture').play();
turnText.setText('Black Princess eliminates 2 pieces!');
// Switch turns
currentPlayer = 'white';
LK.setTimeout(function () {
turnText.setText('Your Turn');
}, 1000);
}, 800);
return {
piece: piece,
isElimination: true
}; // Special marker for elimination
}
}
}
}
return null;
}
function findBestDefensiveMove(validMoves) {
var bestDefense = null;
var bestScore = -1000;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var score = 0;
// Check if this move gets us out of check
if (simulateMove(move, function () {
return !isInCheck('black');
})) {
score += 100;
// Prefer counter-attacks when escaping check
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
score += getPieceValue(targetPiece.pieceType) * 10;
}
if (score > bestScore) {
bestScore = score;
bestDefense = move;
}
}
}
return bestDefense;
}
// Enhanced piece-specific evaluation functions
function evaluateKnightMove(move) {
var score = 0;
// Knights are effective in closed positions and as outposts
var distanceFromCenter = Math.abs(move.toRow - 3.5) + Math.abs(move.toCol - 7.5);
if (distanceFromCenter < 4) {
score += 10; // Favor central knight positions
}
return score;
}
function evaluateRookMove(move) {
var score = 0;
// Rooks benefit from open files and ranks
var openFile = true;
var openRank = true;
// Check if file is open
for (var row = 0; row < 8; row++) {
if (row !== move.toRow) {
var piece = getPieceAt(row, move.toCol);
if (piece && piece.pieceColor === move.piece.pieceColor) {
openFile = false;
break;
}
}
}
// Check if rank is open
for (var col = 0; col < 16; col++) {
if (col !== move.toCol) {
var piece = getPieceAt(move.toRow, col);
if (piece && piece.pieceColor === move.piece.pieceColor) {
openRank = false;
break;
}
}
}
if (openFile) score += 15;
if (openRank) score += 10;
return score;
}
function evaluateBishopMove(move) {
var score = 0;
// Bishops benefit from long diagonals
var diagonalLength = Math.min(Math.min(move.toRow, 7 - move.toRow) + Math.min(move.toCol, 15 - move.toCol), Math.min(move.toRow, 7 - move.toRow) + Math.min(15 - move.toCol, move.toCol));
score += diagonalLength * 2;
return score;
}
function evaluatePawnMove(move) {
var score = 0;
// Pawns should advance toward promotion
var advancement = move.piece.pieceColor === 'black' ? move.toRow - move.piece.row : move.piece.row - move.toRow;
if (advancement > 0) {
score += advancement * 5;
}
// Near promotion gets huge bonus
if (move.piece.pieceColor === 'black' && move.toRow >= 6 || move.piece.pieceColor === 'white' && move.toRow <= 1) {
score += 50;
}
return score;
}
function findCheckmateInOne(validMoves) {
// Find immediate checkmate moves - highest priority
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
if (simulateMove(move, function () {
if (isInCheck('white')) {
// Check if white has any escape moves
var whiteMoves = getAllValidMoves('white');
for (var j = 0; j < whiteMoves.length; j++) {
if (canEscapeCheck(whiteMoves[j].piece, whiteMoves[j].toRow, whiteMoves[j].toCol)) {
return false; // Not checkmate - escape exists
}
}
return true; // Checkmate confirmed
}
return false;
})) {
return move; // Found checkmate in one
}
}
return null;
}
function preventOpponentCheckmate(validMoves) {
// Prevent opponent from achieving checkmate next move
var opponentThreats = getAllValidMoves('white');
for (var i = 0; i < opponentThreats.length; i++) {
var threatMove = opponentThreats[i];
// Check if opponent threat leads to our checkmate
var wouldBeCheckmate = simulateOpponentMove(threatMove, function () {
if (isInCheck('black')) {
var blackMoves = getAllValidMoves('black');
for (var j = 0; j < blackMoves.length; j++) {
if (canEscapeCheck(blackMoves[j].piece, blackMoves[j].toRow, blackMoves[j].toCol)) {
return false; // We can escape
}
}
return true; // We would be checkmated
}
return false;
});
if (wouldBeCheckmate) {
// Find move that prevents this checkmate
for (var k = 0; k < validMoves.length; k++) {
var preventMove = validMoves[k];
if (simulateMove(preventMove, function () {
return !simulateOpponentMove(threatMove, function () {
return isInCheck('black');
});
})) {
return preventMove; // This move prevents checkmate
}
}
}
}
return null;
}
function simulateOpponentMove(opponentMove, testFunction) {
// Temporarily simulate opponent's move
var originalRow = opponentMove.piece.row;
var originalCol = opponentMove.piece.col;
var capturedPiece = getPieceAt(opponentMove.toRow, opponentMove.toCol);
gameBoard[originalRow][originalCol] = null;
gameBoard[opponentMove.toRow][opponentMove.toCol] = opponentMove.piece;
opponentMove.piece.row = opponentMove.toRow;
opponentMove.piece.col = opponentMove.toCol;
var result = testFunction();
// Restore position
opponentMove.piece.row = originalRow;
opponentMove.piece.col = originalCol;
gameBoard[originalRow][originalCol] = opponentMove.piece;
gameBoard[opponentMove.toRow][opponentMove.toCol] = capturedPiece;
return result;
}
function findBestTacticalCombination(validMoves) {
// Find complex tactical sequences - pins, forks, discoveries, sacrifices
var bestTactical = null;
var bestTacticalValue = 0;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var tacticalValue = 0;
// Check for forcing sequences
if (isMoveForcingSequence(move)) {
tacticalValue += 50;
}
// Check for piece sacrifice that leads to mate
if (isBrilliantSacrifice(move)) {
tacticalValue += 100;
}
// Check for advanced pin patterns
if (createsAdvancedPin(move)) {
tacticalValue += 40;
}
// Check for discovered attack combinations
if (createsDiscoveredCombination(move)) {
tacticalValue += 60;
}
// Check for deflection tactics
if (createsDeflection(move)) {
tacticalValue += 35;
}
if (tacticalValue > bestTacticalValue) {
bestTacticalValue = tacticalValue;
bestTactical = move;
}
}
return bestTacticalValue >= 30 ? bestTactical : null;
}
function isMoveForcingSequence(move) {
// Check if move forces opponent into bad position
return simulateMove(move, function () {
var opponentMoves = getAllValidMoves('white');
var forcedBadMoves = 0;
for (var i = 0; i < opponentMoves.length; i++) {
var opMove = opponentMoves[i];
if (simulateOpponentMove(opMove, function () {
return isInCheck('white') || isPieceUnderThreat(opMove.piece);
})) {
forcedBadMoves++;
}
}
return forcedBadMoves >= opponentMoves.length * 0.7; // 70% of moves are bad
});
}
function isBrilliantSacrifice(move) {
// Check for brilliant sacrifices that lead to winning positions
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (!targetPiece) return false;
var sacrificeValue = getPieceValue(move.piece.pieceType);
var gainValue = getPieceValue(targetPiece.pieceType);
// Only consider if we're sacrificing material
if (sacrificeValue <= gainValue) return false;
return simulateMove(move, function () {
// Check if sacrifice leads to overwhelming position or mate threat
var ourAdvantageAfter = calculatePositionalAdvantage('black');
var opponentMoves = getAllValidMoves('white');
// If opponent has very limited good responses, sacrifice is brilliant
var goodOpponentMoves = 0;
for (var i = 0; i < opponentMoves.length; i++) {
if (!isPieceUnderThreatAt(opponentMoves[i].piece, opponentMoves[i].toRow, opponentMoves[i].toCol)) {
goodOpponentMoves++;
}
}
return ourAdvantageAfter > 30 || goodOpponentMoves <= 2;
});
}
function calculatePositionalAdvantage(color) {
var advantage = 0;
var ourColor = color;
var enemyColor = color === 'white' ? 'black' : 'white';
// Material count
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
var value = getPieceValue(piece.pieceType);
if (piece.pieceColor === ourColor) {
advantage += value;
} else {
advantage -= value;
}
}
// Positional factors
var ourKing = findKing(ourColor);
var enemyKing = findKing(enemyColor);
if (ourKing && enemyKing) {
// King safety difference
advantage += (countThreatsToKing(enemyColor) - countThreatsToKing(ourColor)) * 5;
// Center control
var ourCenterControl = 0;
var enemyCenterControl = 0;
var centerSquares = [{
row: 3,
col: 7
}, {
row: 3,
col: 8
}, {
row: 4,
col: 7
}, {
row: 4,
col: 8
}];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
for (var j = 0; j < centerSquares.length; j++) {
if (piece.canMoveTo(centerSquares[j].row, centerSquares[j].col)) {
if (piece.pieceColor === ourColor) {
ourCenterControl++;
} else {
enemyCenterControl++;
}
}
}
}
advantage += (ourCenterControl - enemyCenterControl) * 2;
}
return advantage;
}
function calculatePerfectFork(move) {
// Calculate exact value of fork attacks
var forkTargets = [];
return simulateMove(move, function () {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'white' && move.piece.canMoveTo(piece.row, piece.col)) {
forkTargets.push(getPieceValue(piece.pieceType));
}
}
// Return value of second-best target (since we can only capture one)
if (forkTargets.length >= 2) {
forkTargets.sort(function (a, b) {
return b - a;
});
return forkTargets[1]; // Second highest value
}
return 0;
});
}
function createsPerfectPin(move) {
var enemyKing = findKing('white');
if (!enemyKing) return false;
return simulateMove(move, function () {
// Check if move creates pin against enemy king
var direction = {
row: enemyKing.row - move.toRow,
col: enemyKing.col - move.toCol
};
// Normalize direction
var gcd = Math.abs(direction.row) || Math.abs(direction.col);
if (gcd > 0) {
direction.row = direction.row / gcd;
direction.col = direction.col / gcd;
}
// Check if there's exactly one enemy piece between attacker and king
var currentRow = move.toRow + direction.row;
var currentCol = move.toCol + direction.col;
var blockerFound = false;
while (currentRow !== enemyKing.row || currentCol !== enemyKing.col) {
var piece = getPieceAt(currentRow, currentCol);
if (piece) {
if (piece.pieceColor === 'white' && !blockerFound) {
blockerFound = true; // First blocker found
} else {
return false; // Multiple pieces or our piece blocking
}
}
currentRow += direction.row;
currentCol += direction.col;
if (currentRow < 0 || currentRow >= 8 || currentCol < 0 || currentCol >= 16) break;
}
return blockerFound;
});
}
function createsPerfectDiscovery(move) {
// Check for discovered attacks with perfect accuracy
var originalRow = move.piece.row;
var originalCol = move.piece.col;
return simulateMove(move, function () {
// Check if moving this piece discovers an attack from another piece
for (var i = 0; i < pieces.length; i++) {
var ally = pieces[i];
if (ally.pieceColor === 'black' && ally !== move.piece) {
// Temporarily put moving piece back
gameBoard[move.toRow][move.toCol] = null;
gameBoard[originalRow][originalCol] = move.piece;
move.piece.row = originalRow;
move.piece.col = originalCol;
var couldAttackBefore = false;
for (var j = 0; j < pieces.length; j++) {
var enemy = pieces[j];
if (enemy.pieceColor === 'white' && ally.canMoveTo(enemy.row, enemy.col)) {
couldAttackBefore = true;
break;
}
}
// Restore position
gameBoard[originalRow][originalCol] = null;
gameBoard[move.toRow][move.toCol] = move.piece;
move.piece.row = move.toRow;
move.piece.col = move.toCol;
if (!couldAttackBefore) {
// Check if ally can attack enemy pieces now
for (var j = 0; j < pieces.length; j++) {
var enemy = pieces[j];
if (enemy.pieceColor === 'white' && ally.canMoveTo(enemy.row, enemy.col)) {
return true; // Discovered attack found
}
}
}
}
}
return false;
});
}
function createsPerfectDeflection(move) {
// Check if move deflects a key defender
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (!targetPiece || targetPiece.pieceColor !== 'white') return false;
return simulateMove(move, function () {
// Check if capturing this piece leaves important pieces undefended
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'white' && piece !== targetPiece) {
var wasDefended = targetPiece.canMoveTo(piece.row, piece.col);
if (wasDefended) {
// Check if piece is now undefended and can be captured
var isNowUndefended = true;
for (var j = 0; j < pieces.length; j++) {
var defender = pieces[j];
if (defender.pieceColor === 'white' && defender !== targetPiece && defender.canMoveTo(piece.row, piece.col)) {
isNowUndefended = false;
break;
}
}
if (isNowUndefended) {
return true; // Successful deflection
}
}
}
}
return false;
});
}
function createsDeflection(move) {
// Check if move deflects a key defender piece
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (!targetPiece || targetPiece.pieceColor !== 'black') return false;
return simulateMove(move, function () {
// Check if capturing this piece leaves important pieces undefended
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'black' && piece !== targetPiece) {
var wasDefended = targetPiece.canMoveTo(piece.row, piece.col);
if (wasDefended) {
// Check if piece is now undefended and can be captured
var isNowUndefended = true;
for (var j = 0; j < pieces.length; j++) {
var defender = pieces[j];
if (defender.pieceColor === 'black' && defender !== targetPiece && defender.canMoveTo(piece.row, piece.col)) {
isNowUndefended = false;
break;
}
}
if (isNowUndefended) {
return true; // Successful deflection
}
}
}
}
return false;
});
}
function createsDiscoveredCombination(move) {
// Check for discovered attack combinations - complex tactical patterns
var originalRow = move.piece.row;
var originalCol = move.piece.col;
return simulateMove(move, function () {
// Check if moving this piece creates multiple discovered attacks
var discoveredAttacks = 0;
var highValueTargets = 0;
// Check each ally piece to see if it gains new attacking opportunities
for (var i = 0; i < pieces.length; i++) {
var ally = pieces[i];
if (ally.pieceColor === 'black' && ally !== move.piece) {
// Temporarily put moving piece back to check what was blocked before
gameBoard[move.toRow][move.toCol] = null;
gameBoard[originalRow][originalCol] = move.piece;
move.piece.row = originalRow;
move.piece.col = originalCol;
var enemyTargetsBefore = [];
for (var j = 0; j < pieces.length; j++) {
var enemy = pieces[j];
if (enemy.pieceColor === 'white' && ally.canMoveTo(enemy.row, enemy.col)) {
enemyTargetsBefore.push(enemy);
}
}
// Restore position after move
gameBoard[originalRow][originalCol] = null;
gameBoard[move.toRow][move.toCol] = move.piece;
move.piece.row = move.toRow;
move.piece.col = move.toCol;
var enemyTargetsAfter = [];
for (var j = 0; j < pieces.length; j++) {
var enemy = pieces[j];
if (enemy.pieceColor === 'white' && ally.canMoveTo(enemy.row, enemy.col)) {
enemyTargetsAfter.push(enemy);
}
}
// Check for new attacks that weren't possible before
for (var k = 0; k < enemyTargetsAfter.length; k++) {
var newTarget = enemyTargetsAfter[k];
var wasTargetedBefore = false;
for (var l = 0; l < enemyTargetsBefore.length; l++) {
if (enemyTargetsBefore[l] === newTarget) {
wasTargetedBefore = true;
break;
}
}
if (!wasTargetedBefore) {
discoveredAttacks++;
var targetValue = getPieceValue(newTarget.pieceType);
if (targetValue >= 5) {
// Queen, rook, or higher value pieces
highValueTargets++;
}
}
}
}
}
// Discovered combination is valuable if it creates multiple new attacks
// or targets high-value pieces
return discoveredAttacks >= 2 || highValueTargets >= 1;
});
}
function createsAdvancedPin(move) {
// Check for advanced pin patterns - more sophisticated than basic pins
var enemyKing = findKing('white');
if (!enemyKing) return false;
return simulateMove(move, function () {
// Check if move creates a pin that restricts enemy piece movement
for (var i = 0; i < pieces.length; i++) {
var enemyPiece = pieces[i];
if (enemyPiece.pieceColor === 'white' && enemyPiece.pieceType !== 'king') {
// Check if this enemy piece is now pinned to their king
var direction = {
row: enemyKing.row - enemyPiece.row,
col: enemyKing.col - enemyPiece.col
};
// Normalize direction
var maxDist = Math.max(Math.abs(direction.row), Math.abs(direction.col));
if (maxDist > 0) {
direction.row = Math.round(direction.row / maxDist);
direction.col = Math.round(direction.col / maxDist);
// Check if our piece can attack along this line through the pinned piece to the king
var canPinThroughPiece = false;
if (move.piece.canMoveTo(enemyKing.row, enemyKing.col)) {
// Temporarily remove the potentially pinned piece
var originalRow = enemyPiece.row;
var originalCol = enemyPiece.col;
gameBoard[originalRow][originalCol] = null;
// Check if our piece can still attack the king
if (move.piece.canMoveTo(enemyKing.row, enemyKing.col)) {
canPinThroughPiece = true;
}
// Restore the piece
gameBoard[originalRow][originalCol] = enemyPiece;
}
if (canPinThroughPiece) {
// Verify the pin is along a straight line (rook/queen) or diagonal (bishop/queen)
var rowDiff = Math.abs(move.toRow - enemyKing.row);
var colDiff = Math.abs(move.toCol - enemyKing.col);
var isStraightLine = rowDiff === 0 || colDiff === 0;
var isDiagonal = rowDiff === colDiff;
if (move.piece.pieceType === 'rook' && isStraightLine || move.piece.pieceType === 'bishop' && isDiagonal || move.piece.pieceType === 'quenn' && (isStraightLine || isDiagonal)) {
return true; // Advanced pin created
}
}
}
}
}
return false;
});
}
function createsPerfectSkewer(move) {
// Check for skewer patterns (attacking valuable piece in front of less valuable)
return simulateMove(move, function () {
for (var i = 0; i < pieces.length; i++) {
var frontPiece = pieces[i];
if (frontPiece.pieceColor === 'white' && move.piece.canMoveTo(frontPiece.row, frontPiece.col)) {
// Check if there's a less valuable piece behind this one
var direction = {
row: frontPiece.row - move.toRow,
col: frontPiece.col - move.toCol
};
var gcd = Math.max(Math.abs(direction.row), Math.abs(direction.col));
if (gcd > 0) {
direction.row = Math.round(direction.row / gcd);
direction.col = Math.round(direction.col / gcd);
var checkRow = frontPiece.row + direction.row;
var checkCol = frontPiece.col + direction.col;
while (checkRow >= 0 && checkRow < 8 && checkCol >= 0 && checkCol < 16) {
var behindPiece = getPieceAt(checkRow, checkCol);
if (behindPiece && behindPiece.pieceColor === 'white') {
var frontValue = getPieceValue(frontPiece.pieceType);
var behindValue = getPieceValue(behindPiece.pieceType);
if (frontValue > behindValue || frontPiece.pieceType === 'king') {
return true; // Successful skewer
}
break;
} else if (behindPiece) {
break; // Our piece blocking
}
checkRow += direction.row;
checkCol += direction.col;
}
}
}
}
return false;
});
}
function findOptimalDefensiveMove(validMoves) {
// Find the best defensive move with perfect calculation
var bestDefense = null;
var bestDefenseScore = -10000;
for (var i = 0; i < validMoves.length; i++) {
var move = validMoves[i];
var defenseScore = 0;
// Check if move gets us out of check
if (simulateMove(move, function () {
return !isInCheck('black');
})) {
defenseScore += 1000;
// Bonus for counter-attacking while defending
var targetPiece = getPieceAt(move.toRow, move.toCol);
if (targetPiece && targetPiece.pieceColor === 'white') {
defenseScore += getPieceValue(targetPiece.pieceType) * 50;
// Extra bonus for capturing the threatening piece
var blackKing = findKing('black');
var threateningPiece = findThreateningPiece(blackKing);
if (targetPiece === threateningPiece) {
defenseScore += 500; // Eliminate the threat directly
}
}
// Bonus for creating counter-threats
if (simulateMove(move, function () {
return isInCheck('white');
})) {
defenseScore += 200;
}
}
if (defenseScore > bestDefenseScore) {
bestDefenseScore = defenseScore;
bestDefense = move;
}
}
return bestDefense;
}
function updateQueenSnakePinkSquares() {
// Clear existing pink squares
for (var i = 0; i < queenSnakePinkSquares.length; i++) {
if (!queenSnakePinkSquares[i].destroyed) {
queenSnakePinkSquares[i].destroy();
}
}
queenSnakePinkSquares = [];
// Add pink squares for Queen and Snake pieces when stamina is 0
if (staminaPoints === 0) {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'white' && (piece.pieceType === 'queen' || piece.pieceType === 'snake')) {
var pinkSquare = LK.getAsset('staminaBar', {
anchorX: 0,
anchorY: 0,
alpha: 0.4,
scaleX: 128 / 40,
// Scale to fit square size
scaleY: 128 / 40
});
pinkSquare.x = boardStartX + piece.col * squareSize;
pinkSquare.y = boardStartY + piece.row * squareSize;
game.addChild(pinkSquare);
queenSnakePinkSquares.push(pinkSquare);
// Animate pink square appearance
tween(pinkSquare, {
alpha: 0.6
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
// Add pink squares for AI Queen and Snake pieces when AI stamina is 0
if (aiStaminaPoints === 0) {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === 'black' && (piece.pieceType === 'queen' || piece.pieceType === 'snake')) {
var pinkSquare = LK.getAsset('staminaBar', {
anchorX: 0,
anchorY: 0,
alpha: 0.4,
scaleX: 128 / 40,
// Scale to fit square size
scaleY: 128 / 40
});
pinkSquare.x = boardStartX + piece.col * squareSize;
pinkSquare.y = boardStartY + piece.row * squareSize;
game.addChild(pinkSquare);
queenSnakePinkSquares.push(pinkSquare);
// Animate pink square appearance
tween(pinkSquare, {
alpha: 0.6
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
} ===================================================================
--- original.js
+++ change.js
@@ -1461,8 +1461,18 @@
snakeMovesRemaining = maxMoves - piece.snakeMovesThisTurn;
currentSnakePiece = piece;
// Apply poison to pieces around the snake's new position
applyPoisonBehindSnake(piece, newRow, newCol);
+ // Handle additional stamina cost for poisoning
+ if (willPoison) {
+ // Deduct additional stamina for poisoning
+ if (piece.pieceColor === 'white' && staminaPoints > 0) {
+ staminaPoints--;
+ updateStaminaDisplay();
+ } else if (piece.pieceColor === 'black' && aiStaminaPoints > 0) {
+ aiStaminaPoints--;
+ }
+ }
// If poisoning occurred, immediately switch turns after 2 moves
if (willPoison) {
// Snake completed poisoning moves - put on cooldown and reset
piece.snakeMovesThisTurn = 0;
a black chess piece that resembles a chess pawn but is dressed like a jester. In-Game asset. 2d. High contrast. No shadows
Draw a black pawn dressed in a priest's robe and holding two crosses in the air in a divine manner. In-Game asset. 2d. High contrast. No shadows
Draw a white pawn dressed in a priest's robe and holding two crosses in the air in a divine manner. In-Game asset. 2d. High contrast. No shadows