/****
* 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; // 0 = white, 1 = black
self.boardRow = row;
self.boardCol = col;
self.hasMoved = false;
self.isPromoted = false; // Track permanent promotion status for vizier
self.hasTeleported = false; // Track if pawn's pawn has used teleportation
self.mustExitSaray = false; // Track if piece must exit saray after capture
var pieceGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
if (color === 1) {
pieceGraphics.tint = 0x333333;
}
self.moveTo = function (newRow, newCol) {
self.boardRow = newRow;
self.boardCol = newCol;
self.hasMoved = true;
var boardPos = getBoardPosition(newRow, newCol);
tween(self, {
x: boardPos.x,
y: boardPos.y
}, {
duration: 300
});
};
self.getRawMoves = function () {
var moves = [];
var directions = [];
switch (self.pieceType) {
case 'pawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'rook':
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
for (var dist = 1; dist < 11; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
break;
case 'bishop':
// Bishop can move one square orthogonally (row/column only) - not allowed in traditional version
if (!isTraditionalVersion) {
var oneSquareOrthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
// Add one square moves in orthogonal directions only
for (var i = 0; i < oneSquareOrthogonalDirections.length; i++) {
var dir = oneSquareOrthogonalDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// Bishop can also move as many squares as wanted diagonally (minimum 2) without jumping over friendly pieces
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
for (var dist = 2; dist < 11; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
// Check if path is blocked by any piece (friendly or enemy)
var pathBlocked = false;
for (var checkDist = 1; checkDist < dist; checkDist++) {
var checkRow = self.boardRow + dir.dr * checkDist;
var checkCol = self.boardCol + dir.dc * checkDist;
var pathPiece = getPieceAt(checkRow, checkCol);
if (pathPiece) {
pathBlocked = true;
break;
}
}
if (pathBlocked) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
break;
case 'queen':
// Vizier: restricted to one square, enhanced to eight squares when promoted
// Diagonal movement is closed at the beginning (only orthogonal moves allowed)
// When promoted, both orthogonal and diagonal moves are allowed
if (self.isPromoted) {
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
} else {
// Only orthogonal moves at the beginning (diagonals closed)
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
}
var maxDistance = self.isPromoted ? 8 : 1;
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
for (var dist = 1; dist <= maxDistance; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
break;
case 'king':
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'knight':
var knightMoves = [{
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}, {
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
}, {
dr: 1,
dc: 2
}, {
dr: 1,
dc: -2
}, {
dr: -1,
dc: 2
}, {
dr: -1,
dc: -2
}];
for (var i = 0; i < knightMoves.length; i++) {
var move = knightMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'warElephant':
if (isTraditionalVersion) {
// Traditional version: elephant can only jump 2 squares diagonally
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var dist = 2; // Only 2 squares in traditional version
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
continue;
}
// Elephant can jump over pieces
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
} else {
// Modern version: Moves 1 or 2 steps row, column, and diagonal with jumping ability
// Elephant cannot be stopped by any pieces (friendly or enemy)
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
for (var dist = 1; dist <= 2; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
// Elephant ignores all pieces and can move through them
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
// Continue moving even if there's a piece - elephant cannot be stopped
}
}
}
break;
case 'siegeTower':
if (isTraditionalVersion) {
// Traditional version: siegeTower can only skip 2 squares in columns and rows
var orthogonalDirections = [{
dr: 2,
dc: 0
}, {
dr: -2,
dc: 0
}, {
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}];
for (var i = 0; i < orthogonalDirections.length; i++) {
var orthDir = orthogonalDirections[i];
var newRow = self.boardRow + orthDir.dr;
var newCol = self.boardCol + orthDir.dc;
if (!isValidSquare(newRow, newCol)) {
continue;
}
// Siege tower can jump over pieces in traditional version
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
} else {
// Modern version: Must move 1 square in row/column, then 1 square diagonally
// Captures enemy pieces in its path
// siegeTower cannot be stopped by enemies
var orthogonalDirections = [{
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}];
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
// Try L-shaped moves: orthogonal then diagonal
for (var i = 0; i < orthogonalDirections.length; i++) {
var orthDir = orthogonalDirections[i];
var firstRow = self.boardRow + orthDir.dr;
var firstCol = self.boardCol + orthDir.dc;
if (!isValidSquare(firstRow, firstCol)) {
continue;
}
var firstPiece = getPieceAt(firstRow, firstCol);
// siegeTower cannot be stopped by enemies, but friendly pieces still block
if (firstPiece && firstPiece.pieceColor === self.pieceColor) {
// Can't move through friendly piece, skip this direction
continue;
}
// Try diagonal moves from the orthogonal position
for (var j = 0; j < diagonalDirections.length; j++) {
var diagDir = diagonalDirections[j];
var finalRow = firstRow + diagDir.dr;
var finalCol = firstCol + diagDir.dc;
if (!isValidSquare(finalRow, finalCol)) {
continue;
}
var finalPiece = getPieceAt(finalRow, finalCol);
if (!finalPiece || finalPiece.pieceColor !== self.pieceColor) {
var moveData = {
row: finalRow,
col: finalCol,
capturesInPath: []
};
// Add captured pieces in path
if (firstPiece && firstPiece.pieceColor !== self.pieceColor) {
moveData.capturesInPath.push({
row: firstRow,
col: firstCol
});
}
if (finalPiece && finalPiece.pieceColor !== self.pieceColor) {
moveData.capturesInPath.push({
row: finalRow,
col: finalCol
});
}
moves.push(moveData);
}
}
}
// If friendly piece in orthogonal path, allow simple orthogonal move
for (var i = 0; i < orthogonalDirections.length; i++) {
var orthDir = orthogonalDirections[i];
var newRow = self.boardRow + orthDir.dr;
var newCol = self.boardCol + orthDir.dc;
if (!isValidSquare(newRow, newCol)) {
continue;
}
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'minister':
// Minister: 1 square diagonally, when promoted moves like rook and knight with jumping
if (self.isPromoted) {
// Rook-like moves (can jump over pieces when promoted)
var rookDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < rookDirections.length; i++) {
var dir = rookDirections[i];
for (var dist = 1; dist < 11; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
// Knight-like moves (can jump over pieces)
var knightMoves = [{
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}, {
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
}, {
dr: 1,
dc: 2
}, {
dr: 1,
dc: -2
}, {
dr: -1,
dc: 2
}, {
dr: -1,
dc: -2
}];
for (var i = 0; i < knightMoves.length; i++) {
var move = knightMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else {
// Standard minister: 1 square diagonally
directions = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
break;
case 'royalGuard':
// Up to 3 squares diagonally with blockage of friendly pieces
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
for (var dist = 1; dist <= 3; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
// Only 2 squares along rows and columns (no 1-step moves)
var orthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < orthogonalDirections.length; i++) {
var dir = orthogonalDirections[i];
// Only allow 2-step moves in orthogonal directions
var dist = 2;
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// Forward or backward L-shaped moves (no left/right L moves)
var lShapeMoves = [
// Forward L-shapes
{
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
},
// Backward L-shapes
{
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}];
for (var i = 0; i < lShapeMoves.length; i++) {
var move = lShapeMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'giraffe':
// Giraffe: one diagonal move followed by 3-10 straight moves with obstacle checking
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var diagDir = diagonalDirections[i];
var firstRow = self.boardRow + diagDir.dr;
var firstCol = self.boardCol + diagDir.dc;
if (!isValidSquare(firstRow, firstCol)) {
continue;
}
var firstPiece = getPieceAt(firstRow, firstCol);
if (firstPiece) {
continue; // First diagonal square must be empty
}
// Determine straight directions based on diagonal direction
var straightDirections = [];
if (diagDir.dr > 0) {
straightDirections.push({
dr: 1,
dc: 0
}); // downward
}
if (diagDir.dr < 0) {
straightDirections.push({
dr: -1,
dc: 0
}); // upward
}
if (diagDir.dc > 0) {
straightDirections.push({
dr: 0,
dc: 1
}); // rightward
}
if (diagDir.dc < 0) {
straightDirections.push({
dr: 0,
dc: -1
}); // leftward
}
// Try straight moves from 3 to 10 squares in each valid direction
for (var j = 0; j < straightDirections.length; j++) {
var straightDir = straightDirections[j];
for (var step = 3; step <= 10; step++) {
var newRow = firstRow + straightDir.dr * step;
var newCol = firstCol + straightDir.dc * step;
if (!isValidSquare(newRow, newCol)) {
break;
}
// Check if path is clear from diagonal position to target
var pathClear = true;
for (var checkStep = 1; checkStep <= step; checkStep++) {
var checkRow = firstRow + straightDir.dr * checkStep;
var checkCol = firstCol + straightDir.dc * checkStep;
var checkPiece = getPieceAt(checkRow, checkCol);
if (checkPiece) {
if (checkStep === step && checkPiece.pieceColor !== self.pieceColor) {
// Can capture enemy piece at final position
moves.push({
row: newRow,
col: newCol
});
}
pathClear = false;
break;
}
}
if (pathClear) {
moves.push({
row: newRow,
col: newCol
});
}
if (!pathClear) {
break; // Stop checking longer distances in this direction
}
}
}
}
break;
case 'bull':
// Bull can move one column forward or backward
var columnDirections = [{
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < columnDirections.length; i++) {
var colDir = columnDirections[i];
var newRow = self.boardRow + colDir.dr;
var newCol = self.boardCol + colDir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// Bull moves one square forward or backward in column, then unlimited diagonal movement in same direction
for (var i = 0; i < columnDirections.length; i++) {
var colDir = columnDirections[i];
var firstRow = self.boardRow + colDir.dr;
var firstCol = self.boardCol + colDir.dc;
if (!isValidSquare(firstRow, firstCol)) {
continue;
}
var firstPiece = getPieceAt(firstRow, firstCol);
if (firstPiece) {
continue; // First square must be empty for bull movement
}
// From the first square, move diagonally unlimited in the forward/backward direction
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var j = 0; j < diagonalDirections.length; j++) {
var diagDir = diagonalDirections[j];
// Only allow diagonal movement in same forward/backward direction as column move
if (colDir.dr > 0 && diagDir.dr <= 0 || colDir.dr < 0 && diagDir.dr >= 0) {
continue; // Skip diagonal directions that don't match column direction
}
// Move unlimited squares diagonally from first position
for (var dist = 1; dist < 11; dist++) {
var finalRow = firstRow + diagDir.dr * dist;
var finalCol = firstCol + diagDir.dc * dist;
if (!isValidSquare(finalRow, finalCol)) {
break;
}
var finalPiece = getPieceAt(finalRow, finalCol);
if (finalPiece) {
if (finalPiece.pieceColor !== self.pieceColor) {
moves.push({
row: finalRow,
col: finalCol
});
}
break;
}
moves.push({
row: finalRow,
col: finalCol
});
}
}
}
// Bull can also jump two squares left and right
var leftRightJumps = [{
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}];
for (var i = 0; i < leftRightJumps.length; i++) {
var jump = leftRightJumps[i];
var newRow = self.boardRow + jump.dr;
var newCol = self.boardCol + jump.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'camel':
// 1 square column and row (orthogonal moves) - not allowed in traditional version
if (!isTraditionalVersion) {
var orthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < orthogonalDirections.length; i++) {
var dir = orthogonalDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// 1 square long L (knight-like moves)
var knightMoves = [{
dr: 3,
dc: 1
}, {
dr: 3,
dc: -1
}, {
dr: -3,
dc: 1
}, {
dr: -3,
dc: -1
}, {
dr: 1,
dc: 3
}, {
dr: 1,
dc: -3
}, {
dr: -1,
dc: 3
}, {
dr: -1,
dc: -3
}];
for (var i = 0; i < knightMoves.length; i++) {
var move = knightMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'lion':
// Lion has three distinct movement patterns:
// 1. Move 3 squares diagonally in one direction
// 2. L-shaped moves (forward/backward 2 + right/left 1)
// 3. Move 2 diagonally + 1 forward/backward
// 1st Move: 3 squares diagonally (similar to original code)
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var pathBlocked = false;
for (var dist = 1; dist <= 3; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
// 2nd Move: L-shaped moves (forward/backward 2 + right/left 1)
var lShapeMoves = [{
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
}, {
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}];
for (var i = 0; i < lShapeMoves.length; i++) {
var move = lShapeMoves[i];
var midRow = self.boardRow + move.dr / 2;
var endRow = self.boardRow + move.dr;
var endCol = self.boardCol + move.dc;
// Check if vertical path is clear (first two squares)
if (!isValidSquare(midRow, self.boardCol) || getPieceAt(midRow, self.boardCol)) {
continue;
}
// Check for diagonal obstacles
var diagonalBlocked = false;
if (move.dr < 0) {
// moving up
if (move.dc == -1 && getPieceAt(self.boardRow - 1, self.boardCol - 1)) {
diagonalBlocked = true;
}
if (move.dc == 1 && getPieceAt(self.boardRow - 1, self.boardCol + 1)) {
diagonalBlocked = true;
}
} else {
// moving down
if (move.dc == -1 && getPieceAt(self.boardRow + 1, self.boardCol - 1)) {
diagonalBlocked = true;
}
if (move.dc == 1 && getPieceAt(self.boardRow + 1, self.boardCol + 1)) {
diagonalBlocked = true;
}
}
if (diagonalBlocked) {
continue;
}
if (isValidSquare(endRow, endCol)) {
var piece = getPieceAt(endRow, endCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: endRow,
col: endCol
});
}
}
}
// 3rd Move: 2 diagonals + 1 forward/backward
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var pathBlocked = false;
// Check if we can move 2 squares diagonally
for (var checkDist = 1; checkDist <= 2; checkDist++) {
var checkRow = self.boardRow + dir.dr * checkDist;
var checkCol = self.boardCol + dir.dc * checkDist;
if (!isValidSquare(checkRow, checkCol)) {
pathBlocked = true;
break;
}
var pathPiece = getPieceAt(checkRow, checkCol);
if (pathPiece) {
pathBlocked = true;
break;
}
}
if (pathBlocked) {
continue;
}
var twoSquareRow = self.boardRow + dir.dr * 2;
var twoSquareCol = self.boardCol + dir.dc * 2;
// Then move 1 square forward or backward
var verticalMoves = [{
dr: -1,
dc: 0
}, {
dr: 1,
dc: 0
}];
for (var j = 0; j < verticalMoves.length; j++) {
var vertMove = verticalMoves[j];
var finalRow = twoSquareRow + vertMove.dr;
var finalCol = twoSquareCol + vertMove.dc;
if (isValidSquare(finalRow, finalCol)) {
var piece = getPieceAt(finalRow, finalCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: finalRow,
col: finalCol
});
}
}
}
}
// Remove side L-shaped moves from the moves list
moves = moves.filter(function (move) {
return !isSideLMove(self.boardRow, self.boardCol, move.row, move.col);
});
break;
case 'pawnOfTheWarMachine':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawnOfTheWarMachine' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'camelPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'camelPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'ministersPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'ministersPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnOfTheElephant':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawnOfTheElephant' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'shahsade':
// King-like movement: 1 square in any direction
var kingDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// 2 squares diagonally, horizontally, vertically - not allowed in traditional version
if (!isTraditionalVersion) {
var shahsadeDirections = [{
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}, {
dr: 2,
dc: 0
}, {
dr: -2,
dc: 0
}, {
dr: 2,
dc: 2
}, {
dr: 2,
dc: -2
}, {
dr: -2,
dc: 2
}, {
dr: -2,
dc: -2
}];
for (var i = 0; i < shahsadeDirections.length; i++) {
var dir = shahsadeDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
// Check if path is blocked by any piece
var pathBlocked = false;
var stepRow = dir.dr === 0 ? 0 : dir.dr > 0 ? 1 : -1;
var stepCol = dir.dc === 0 ? 0 : dir.dc > 0 ? 1 : -1;
var checkRow = self.boardRow + stepRow;
var checkCol = self.boardCol + stepCol;
var pathPiece = getPieceAt(checkRow, checkCol);
if (pathPiece) {
pathBlocked = true;
}
if (!pathBlocked) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
}
break;
case 'adventurousKing':
// King-like movement: 1 square in any direction
var kingDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// 2 squares diagonally, horizontally, vertically (adventurous Shah can jump over pieces) - not allowed in traditional version
if (!isTraditionalVersion) {
var adventurousKingDirections = [{
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}, {
dr: 2,
dc: 0
}, {
dr: -2,
dc: 0
}, {
dr: 2,
dc: 2
}, {
dr: 2,
dc: -2
}, {
dr: -2,
dc: 2
}, {
dr: -2,
dc: -2
}];
for (var i = 0; i < adventurousKingDirections.length; i++) {
var dir = adventurousKingDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
// Adventurous Shah can jump over pieces - no path blocking check
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
break;
case 'kingsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 7 : 2;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'kingsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'viziersPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'viziersPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'giraffePawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'giraffePawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'knightPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'knightPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'rookPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'rookPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnOfTheLeader':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawnOfTheLeader' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'guardsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
// Forward move (no double move for Guard's Pawn)
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'guardsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnOfTheBull':
var direction = self.pieceColor === 0 ? -1 : 1;
// Forward move (no double move for Pawn of the Bull)
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'pawnOfTheBull' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'lionsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
// Forward move (no double move for Lion's Pawn)
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'lionsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start
if (self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'pawnsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'seaMonster':
var isInLeftRegion = isInRegion(self.boardRow, self.boardCol, 'left');
// 1. ÖZEL YEME ALANI (Görseldeki Kırmızı Bölgeler)
if (isInLeftRegion) {
// Sağa doğru 1, 2 ve 3. kolonları ve onların 1 üst/alt karelerini hedefle
// Bu liste görseldeki kırmızı karelerin tam koordinat karşılığıdır
var specialTargets = [
// 1 Kare Sağa ve Çaprazları
{
dr: 0,
dc: 1
}, {
dr: -1,
dc: 1
}, {
dr: 1,
dc: 1
},
// 2 Kare Sağa ve Çaprazları (Atlayarak)
{
dr: 0,
dc: 2
}, {
dr: -1,
dc: 2
}, {
dr: 1,
dc: 2
},
// 3 Kare Sağa ve Çaprazları (Atlayarak)
{
dr: 0,
dc: 3
}, {
dr: -1,
dc: 3
}, {
dr: 1,
dc: 3
}];
for (var j = 0; j < specialTargets.length; j++) {
var target = specialTargets[j];
var tRow = self.boardRow + target.dr;
var tCol = self.boardCol + target.dc;
if (isValidSquare(tRow, tCol)) {
var targetPiece = getPieceAt(tRow, tCol);
// ÖNEMLİ: Burada 'break' yok! Arada taş olsa bile hedefteki rakibi görür.
if (targetPiece && targetPiece.pieceColor !== self.pieceColor) {
moves.push({
row: tRow,
col: tCol,
isRemoteCapture: true // Yerinden hareket etmeden yer
});
}
}
}
}
// 2. NORMAL DENİZDE HAREKET (Yüzme)
// Sadece boş deniz karelerine gidebilir, taş yiyemez (yeme yukarıda tanımlandı)
var swimDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < swimDirections.length; i++) {
var d = swimDirections[i];
for (var dist = 1; dist <= 3; dist++) {
var sRow = self.boardRow + d.dr * dist;
var sCol = self.boardCol + d.dc * dist;
if (!isValidSquare(sRow, sCol)) break;
var pieceAtSquare = getPieceAt(sRow, sCol);
if (!pieceAtSquare && isSeaSquare(sRow, sCol)) {
moves.push({
row: sRow,
col: sCol
});
} else {
break; // Taş veya kara yolu kapatır
}
}
}
// 3. GENEL YAKIN MESAFE YEME (Dibindeki taşlar için güvenlik)
var neighbors = [{
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var k = 0; k < neighbors.length; k++) {
var nRow = self.boardRow + neighbors[k].dr;
var nCol = self.boardCol + neighbors[k].dc;
if (isValidSquare(nRow, nCol)) {
var nPiece = getPieceAt(nRow, nCol);
if (nPiece && nPiece.pieceColor !== self.pieceColor) {
// Eğer listede yoksa ekle
var alreadyAdded = moves.some(function (m) {
return m.row === nRow && m.col === nCol;
});
if (!alreadyAdded) {
moves.push({
row: nRow,
col: nCol,
isRemoteCapture: true
});
}
}
}
}
break;
}
// Filter out sea squares - pieces cannot enter the sea (except sea monster)
var filteredMoves = [];
for (var i = 0; i < moves.length; i++) {
if (!isSeaSquare(moves[i].row, moves[i].col) || self.pieceType === 'seaMonster') {
filteredMoves.push(moves[i]);
}
}
return filteredMoves;
};
self.getValidMoves = function () {
var moves = self.getRawMoves();
// Add castling for king after getting raw moves (not allowed in traditional version)
if (self.pieceType === 'king' && !self.hasMoved && !isInCheck(self.pieceColor) && !isTraditionalVersion) {
// Kingside castling - rook is at col + 5 (distance from king to rook)
var kingsideRook = getPieceAt(self.boardRow, self.boardCol + 5);
if (kingsideRook && kingsideRook.pieceType === 'rook' && !kingsideRook.hasMoved) {
var canCastle = true;
// Check squares between king and rook are empty and not attacked
for (var c = self.boardCol + 1; c < self.boardCol + 5; c++) {
if (getPieceAt(self.boardRow, c) || isSquareAttacked(self.boardRow, c, 1 - self.pieceColor)) {
canCastle = false;
break;
}
}
if (canCastle) {
moves.push({
row: self.boardRow,
col: self.boardCol + 2,
isCastling: true,
rookFromCol: self.boardCol + 5,
rookToCol: self.boardCol + 1
});
}
}
// Queenside castling - rook is at col - 5 (distance from king to rook)
var queensideRook = getPieceAt(self.boardRow, self.boardCol - 5);
if (queensideRook && queensideRook.pieceType === 'rook' && !queensideRook.hasMoved) {
var canCastle = true;
// Check squares between king and rook are empty and not attacked
for (var c = self.boardCol - 1; c > self.boardCol - 5; c--) {
if (getPieceAt(self.boardRow, c) || isSquareAttacked(self.boardRow, c, 1 - self.pieceColor)) {
canCastle = false;
break;
}
}
if (canCastle) {
moves.push({
row: self.boardRow,
col: self.boardCol - 2,
isCastling: true,
rookFromCol: self.boardCol - 5,
rookToCol: self.boardCol - 1
});
}
}
}
// Filter moves that would leave the king in check
var validMoves = [];
for (var i = 0; i < moves.length; i++) {
if (isValidMoveConsideringCheck(self, moves[i].row, moves[i].col)) {
// Additional check for sea squares - pieces cannot enter the sea (except sea monster)
if (isSeaSquare(moves[i].row, moves[i].col) && self.pieceType !== 'seaMonster') {
continue; // Skip sea squares
}
// Additional check for palace entry
if (isPalaceSquare(moves[i].row, moves[i].col)) {
if (canEnterPalace(self, moves[i].row, moves[i].col)) {
validMoves.push(moves[i]);
}
} else {
validMoves.push(moves[i]);
}
}
}
return validMoves;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
var BOARD_WIDTH = 15;
var BOARD_HEIGHT = 12;
var SQUARE_SIZE = 140;
var BOARD_START_X = (2048 - BOARD_WIDTH * SQUARE_SIZE) / 2;
var BOARD_START_Y = (2732 - BOARD_HEIGHT * SQUARE_SIZE) / 2 + 100;
var boardSquares = [];
var pieces = [];
var selectedPiece = null;
var highlightedSquares = [];
var currentPlayer = 0; // 0 = white, 1 = black
var gameStarted = false;
var menuVisible = true;
var menuContainer = null;
var lastMove = null;
var shahsadePromoted = [false, false]; // Track if shahsade has been promoted for each player
var pawnsPawnHasUsedSpecialMove = []; // Track which pawn's pawns have used their special move
var pawnsPawnMovementCount = []; // Track how many squares each pawn's pawn has moved
var shahPositionSwapUsed = [false, false]; // Track if Shah position swap has been used for each player
var swapHighlights = []; // Track swap highlight squares
function getBoardPosition(row, col) {
return {
x: BOARD_START_X + (col + 1) * SQUARE_SIZE + SQUARE_SIZE / 2,
y: BOARD_START_Y + row * SQUARE_SIZE + SQUARE_SIZE / 2
};
}
// Region definition system
var regions = {
left: {
name: 'Left Region',
rows: [0, 1],
// Rows A and B (0-indexed)
description: 'The left region of the board consisting of rows A and B'
},
right: {
name: 'Right Region',
rows: [12, 13],
// Rows M and N (0-indexed)
description: 'The right region of the board consisting of rows M and N'
},
upper: {
name: 'Upper Region',
cols: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
// Columns c, d, e, f, g, h, i, j, k, l (0-indexed as 2-11)
description: 'The upper region of the board consisting of columns c1, d1, e1, f1, g1, h1, i1, j1, k1, and l1'
},
lower: {
name: 'Lower Region',
cols: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
// Columns c, d, e, f, g, h, i, j, k, l (0-indexed as 2-11)
description: 'The lower region of the board consisting of columns c10, d10, e10, f10, g10, h10, i10, j10, k10, and l10'
}
};
function isInRegion(row, col, regionName) {
var region = regions[regionName];
if (!region) {
return false;
}
// Check if the row is within the region's defined rows
if (region.rows) {
return region.rows.indexOf(row) !== -1;
}
// Check if the column is within the region's defined columns and row is 1 (c1, d1, etc.) for upper region
if (region.cols && row === 1 && regionName === 'upper') {
return region.cols.indexOf(col) !== -1;
}
// Check if the column is within the region's defined columns and row is 10 (c10, d10, etc.) for lower region
if (region.cols && row === 10 && regionName === 'lower') {
return region.cols.indexOf(col) !== -1;
}
return false;
}
function getRegionName(row, col) {
for (var regionName in regions) {
if (isInRegion(row, col, regionName)) {
return regionName;
}
}
return null; // Not in any defined region
}
function getPiecesInRegion(regionName) {
var piecesInRegion = [];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (isInRegion(piece.boardRow, piece.boardCol, regionName)) {
piecesInRegion.push(piece);
}
}
return piecesInRegion;
}
function addRegion(name, rows, description) {
regions[name] = {
name: name,
rows: rows,
description: description
};
}
function removeRegion(name) {
delete regions[name];
}
var aiMoveTimer = null;
var aiThinkingTime = 1500; // AI thinks for 1.5 seconds
function makeAIMove() {
// Prevent multiple AI moves
if (aiMoveTimer) {
return;
}
// Add thinking delay for better UX
aiMoveTimer = LK.setTimeout(function () {
aiMoveTimer = null;
var bestMove = findBestAIMove(aiColor);
if (bestMove) {
makeMove(bestMove.piece, bestMove.targetRow, bestMove.targetCol, bestMove.moveData);
}
}, aiThinkingTime);
}
function findBestAIMove(color) {
var allMoves = [];
// Collect all possible moves for AI
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === color) {
var moves = piece.getValidMoves();
for (var j = 0; j < moves.length; j++) {
allMoves.push({
piece: piece,
targetRow: moves[j].row,
targetCol: moves[j].col,
moveData: moves[j],
score: evaluateMove(piece, moves[j])
});
}
}
}
if (allMoves.length === 0) {
return null;
}
// Sort moves by score (highest first)
allMoves.sort(function (a, b) {
return b.score - a.score;
});
// Return best move
return allMoves[0];
}
function evaluateMove(piece, move) {
var score = 0;
var targetPiece = getPieceAt(move.row, move.col);
// Prioritize captures
if (targetPiece) {
score += getPieceValue(targetPiece.pieceType) * 10;
// Extra points for capturing king
if (targetPiece.pieceType === 'king') {
score += 1000;
}
}
// Prefer center control
var centerRow = BOARD_HEIGHT / 2;
var centerCol = BOARD_WIDTH / 2;
var distanceFromCenter = Math.abs(move.row - centerRow) + Math.abs(move.col - centerCol);
score += 20 - distanceFromCenter;
// Avoid moving into danger
if (isSquareAttacked(move.row, move.col, 1 - piece.pieceColor)) {
score -= getPieceValue(piece.pieceType) * 5;
}
// Random factor to prevent predictable play
score += Math.random() * 10;
return score;
}
function getPieceValue(pieceType) {
var values = {
'pawn': 1,
'pawnOfTheWarMachine': 1,
'camelPawn': 1,
'ministersPawn': 1,
'pawnOfTheElephant': 1,
'kingsPawn': 1,
'viziersPawn': 1,
'giraffePawn': 1,
'knightPawn': 1,
'rookPawn': 1,
'pawnOfTheLeader': 1,
'guardsPawn': 1,
'pawnOfTheBull': 1,
'lionsPawn': 1,
'pawnsPawn': 1,
'knight': 3,
'bishop': 3,
'minister': 3,
'rook': 5,
'royalGuard': 5,
'giraffe': 5,
'bull': 5,
'camel': 5,
'warElephant': 5,
'siegeTower': 5,
'lion': 5,
'queen': 9,
'seaMonster': 9,
'king': 1000,
'shahsade': 500,
'adventurousKing': 500
};
return values[pieceType] || 1;
}
function getBoardCoordinates(x, y) {
var col = Math.floor((x - BOARD_START_X) / SQUARE_SIZE) - 1;
var row = Math.floor((y - BOARD_START_Y) / SQUARE_SIZE);
return {
row: row,
col: col
};
}
function positionToAlgebraic(row, col) {
var files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'];
var ranks = ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1'];
return files[col] + ranks[row];
}
function pieceToNotation(pieceType) {
var notationMap = {
'king': 'K',
'adventurousKing': 'AK',
'queen': 'V',
'rook': 'R',
'bishop': 'F',
'knight': 'N',
'warElephant': 'E',
'siegeTower': 'W',
'minister': 'F',
'royalGuard': 'RG',
'giraffe': 'G',
'bull': 'T',
'camel': 'C',
'lion': 'L',
'shahsade': 'K',
'pawn': 'PPP',
'pawnOfTheWarMachine': 'WP',
'camelPawn': 'CP',
'ministersPawn': 'FP',
'pawnOfTheElephant': 'EP',
'kingsPawn': 'KP',
'viziersPawn': 'VP',
'giraffePawn': 'GP',
'knightPawn': 'NP',
'rookPawn': 'RP',
'pawnOfTheLeader': 'BP',
'guardsPawn': 'RGP',
'pawnOfTheBull': 'TP',
'lionsPawn': 'LP',
'pawnsPawn': 'PP',
'seaMonster': 'SM'
};
return notationMap[pieceType] || '';
}
function isValidSquare(row, col) {
return row >= 0 && row < BOARD_HEIGHT && col >= -1 && col < BOARD_WIDTH;
}
function isSeaSquare(row, col) {
// Column -1 (column -1) squares are all sea (new column to the left of black palace)
// Column A (column 0) squares are sea except for the palace at row 2
// Column M (column 12) squares are all sea
// Column N (column 13) squares are sea except for the palace at row 9
// Column O (column 14) squares are all sea
// Row 0 is sea for all columns
// Row 11 is sea for all columns
return col === -1 || col === 0 && row !== 2 || col === 12 && row !== 9 || col === 13 || col === 14 || row === 0 || row === 11;
}
function getPieceAt(row, col) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].boardRow === row && pieces[i].boardCol === col) {
return pieces[i];
}
}
return null;
}
function createBoard() {
for (var row = 0; row < BOARD_HEIGHT; row++) {
boardSquares[row] = [];
for (var col = -1; col < BOARD_WIDTH; col++) {
// Sea rows: row 0 (above black pieces) and row 11 (below white pieces)
if (row === 0 || row === 11) {
// Create sea squares for top and bottom rows
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Column -1 squares - create sea squares for all rows in column -1
if (col === -1) {
// All squares in column -1 are sea
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Column A squares - create squares for all valid rows in column 0
if (col === 0) {
// Only row 2 has the black palace, other rows get regular squares
if (row !== 2) {
// Create sea squares for other rows in column A with separate textures
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
}
// Column M squares - create squares for all valid rows in column 12
if (col === 12) {
// Only row 9 has the white palace, other rows get sea squares
if (row !== 9) {
// Create sea squares for other rows in column M with separate textures
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
}
// Column N squares - create sea squares for all rows in column 13
if (col === 13) {
// All squares in column N are sea
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Column O squares - create sea squares for all rows in column 14
if (col === 14) {
// All squares in column O are sea
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Create squares for all rows in column 12
// Special case: Black Palace at column 0, row 2 (left of a10)
// Special case: White Palace at column 13, row 9 (right of l11)
var squareAsset;
if (col === 0 && row === 2) {
squareAsset = 'blackPalace';
} else if (col === 12 && row === 9) {
squareAsset = 'whitePalace';
} else {
var isLight = (row + col) % 2 === 0;
squareAsset = isLight ? 'lightSquare' : 'darkSquare';
}
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
boardSquares[row][col] = square;
}
}
}
function createPieces() {
// Create pieces for both players
var pieceSetup = [
// White pieces (bottom)
{
type: 'rook',
color: 0,
row: 9,
col: 1
}, {
type: 'knight',
color: 0,
row: 9,
col: 2
}, {
type: 'warElephant',
color: 0,
row: 10,
col: 1
}, {
type: 'warElephant',
color: 0,
row: 10,
col: 11
}, {
type: 'bishop',
color: 0,
row: 9,
col: 3
}, {
type: 'minister',
color: 0,
row: 9,
col: 5
}, {
type: 'king',
color: 0,
row: 9,
col: 6
}, {
type: 'bishop',
color: 0,
row: 9,
col: 9
}, {
type: 'siegeTower',
color: 0,
row: 10,
col: 7
}, {
type: 'siegeTower',
color: 0,
row: 10,
col: 5
}, {
type: 'queen',
color: 0,
row: 9,
col: 7
}, {
type: 'giraffe',
color: 0,
row: 9,
col: 4
}, {
type: 'giraffe',
color: 0,
row: 9,
col: 8
}, {
type: 'knight',
color: 0,
row: 9,
col: 10
}, {
type: 'rook',
color: 0,
row: 9,
col: 11
},
// White royal guards
{
type: 'royalGuard',
color: 0,
row: 10,
col: 6
},
// White sea monster (behind royal guard)
{
type: 'seaMonster',
color: 0,
row: 11,
col: 6
},
// White lions
{
type: 'lion',
color: 0,
row: 10,
col: 2
}, {
type: 'lion',
color: 0,
row: 10,
col: 10
},
// White camels
{
type: 'camel',
color: 0,
row: 10,
col: 3
}, {
type: 'camel',
color: 0,
row: 10,
col: 9
},
// White bulls
{
type: 'bull',
color: 0,
row: 10,
col: 4
}, {
type: 'bull',
color: 0,
row: 10,
col: 8
},
// Black pieces (top) - all moved down by 1 row
{
type: 'rook',
color: 1,
row: 2,
col: 1
}, {
type: 'knight',
color: 1,
row: 2,
col: 2
}, {
type: 'warElephant',
color: 1,
row: 1,
col: 1
}, {
type: 'warElephant',
color: 1,
row: 1,
col: 11
}, {
type: 'bishop',
color: 1,
row: 2,
col: 3
}, {
type: 'minister',
color: 1,
row: 2,
col: 7
}, {
type: 'king',
color: 1,
row: 2,
col: 6
}, {
type: 'bishop',
color: 1,
row: 2,
col: 9
}, {
type: 'siegeTower',
color: 1,
row: 1,
col: 7
}, {
type: 'siegeTower',
color: 1,
row: 1,
col: 5
}, {
type: 'queen',
color: 1,
row: 2,
col: 5
}, {
type: 'giraffe',
color: 1,
row: 2,
col: 4
}, {
type: 'giraffe',
color: 1,
row: 2,
col: 8
}, {
type: 'knight',
color: 1,
row: 2,
col: 10
}, {
type: 'rook',
color: 1,
row: 2,
col: 11
},
// Black royal guards
{
type: 'royalGuard',
color: 1,
row: 1,
col: 6
},
// Black sea monster (behind royal guard)
{
type: 'seaMonster',
color: 1,
row: 0,
col: 6
},
// Black lions
{
type: 'lion',
color: 1,
row: 1,
col: 2
}, {
type: 'lion',
color: 1,
row: 1,
col: 10
},
// Black camels
{
type: 'camel',
color: 1,
row: 1,
col: 3
}, {
type: 'camel',
color: 1,
row: 1,
col: 9
},
// Black bulls
{
type: 'bull',
color: 1,
row: 1,
col: 4
}, {
type: 'bull',
color: 1,
row: 1,
col: 8
}];
// Add Pawn of the War Machine for each side
pieceSetup.push({
type: 'pawnOfTheWarMachine',
color: 0,
row: 8,
col: 2
}); // White Pawn of the War Machine
pieceSetup.push({
type: 'pawnOfTheWarMachine',
color: 1,
row: 3,
col: 10
}); // Black Pawn of the War Machine
// Add Camel Pawn for each side
pieceSetup.push({
type: 'camelPawn',
color: 0,
row: 8,
col: 3
}); // White Camel Pawn
pieceSetup.push({
type: 'camelPawn',
color: 1,
row: 3,
col: 9
}); // Black Camel Pawn
// Add Pawn of the Elephant for each side
pieceSetup.push({
type: 'pawnOfTheElephant',
color: 0,
row: 8,
col: 4
}); // White Pawn of the Elephant
pieceSetup.push({
type: 'pawnOfTheElephant',
color: 1,
row: 3,
col: 8
}); // Black Pawn of the Elephant
// Add Minister's Pawn for each side
pieceSetup.push({
type: 'ministersPawn',
color: 0,
row: 8,
col: 5
}); // White Minister's Pawn
pieceSetup.push({
type: 'ministersPawn',
color: 1,
row: 3,
col: 7
}); // Black Minister's Pawn
// Add King's Pawn for each side
pieceSetup.push({
type: 'kingsPawn',
color: 0,
row: 8,
col: 6
}); // White King's Pawn
pieceSetup.push({
type: 'kingsPawn',
color: 1,
row: 3,
col: 6
}); // Black King's Pawn
// Add Vizier's Pawn for each side
pieceSetup.push({
type: 'viziersPawn',
color: 0,
row: 8,
col: 7
}); // White Vizier's Pawn
pieceSetup.push({
type: 'viziersPawn',
color: 1,
row: 3,
col: 5
}); // Black Vizier's Pawn
// Add Giraffe Pawn for each side
pieceSetup.push({
type: 'giraffePawn',
color: 0,
row: 8,
col: 8
}); // White Giraffe Pawn
pieceSetup.push({
type: 'giraffePawn',
color: 1,
row: 3,
col: 4
}); // Black Giraffe Pawn
// Add Knight Pawn for each side
pieceSetup.push({
type: 'knightPawn',
color: 0,
row: 8,
col: 10
}); // White Knight Pawn
pieceSetup.push({
type: 'knightPawn',
color: 1,
row: 3,
col: 2
}); // Black Knight Pawn
// Add Rook Pawn for each side
pieceSetup.push({
type: 'rookPawn',
color: 0,
row: 8,
col: 11
}); // White Rook Pawn
pieceSetup.push({
type: 'rookPawn',
color: 1,
row: 3,
col: 1
}); // Black Rook Pawn
// Add Pawn of the Leader for each side
pieceSetup.push({
type: 'pawnOfTheLeader',
color: 0,
row: 8,
col: 9
}); // White Pawn of the Leader
pieceSetup.push({
type: 'pawnOfTheLeader',
color: 1,
row: 3,
col: 3
}); // Black Pawn of the Leader
// Add Guard's Pawn for each side - in front of the King's Pawn
pieceSetup.push({
type: 'guardsPawn',
color: 0,
row: 7,
col: 6
}); // White Guard's Pawn (in front of White King's Pawn)
pieceSetup.push({
type: 'guardsPawn',
color: 1,
row: 4,
col: 6
}); // Black Guard's Pawn (in front of Black King's Pawn)
// Add Pawn of the Bull for each side - in front of the Pawn of the Leader
pieceSetup.push({
type: 'pawnOfTheBull',
color: 0,
row: 7,
col: 9
}); // White Pawn of the Bull (in front of White Pawn of the Leader)
pieceSetup.push({
type: 'pawnOfTheBull',
color: 1,
row: 4,
col: 3
}); // Black Pawn of the Bull (in front of Black Pawn of the Leader)
// Add Lion's Pawn for each side - in front of the Camel's Pawn
pieceSetup.push({
type: 'lionsPawn',
color: 0,
row: 7,
col: 3
}); // White Lion's Pawn (in front of White Camel Pawn)
pieceSetup.push({
type: 'lionsPawn',
color: 1,
row: 4,
col: 9
}); // Black Lion's Pawn (in front of Black Camel Pawn)
// Add regular pawn for each side
pieceSetup.push({
type: 'pawn',
color: 0,
row: 8,
col: 1
}); // White Pawn
pieceSetup.push({
type: 'pawn',
color: 1,
row: 3,
col: 11
}); // Black Pawn
for (var i = 0; i < pieceSetup.length; i++) {
var setup = pieceSetup[i];
// Skip modern pieces in traditional version
if (isTraditionalVersion) {
var modernPieces = ['lion', 'bull', 'seaMonster', 'royalGuard', 'guardsPawn', 'pawnOfTheBull', 'lionsPawn'];
if (modernPieces.indexOf(setup.type) !== -1) {
continue; // Skip this piece
}
}
var piece = game.addChild(new ChessPiece(setup.type, setup.color, setup.row, setup.col));
var pos = getBoardPosition(setup.row, setup.col);
piece.x = pos.x;
piece.y = pos.y;
pieces.push(piece);
}
}
function clearHighlights() {
for (var i = 0; i < highlightedSquares.length; i++) {
highlightedSquares[i].destroy();
}
highlightedSquares = [];
}
function clearSwapHighlights() {
for (var i = 0; i < swapHighlights.length; i++) {
swapHighlights[i].destroy();
}
swapHighlights = [];
}
function highlightMoves(moves) {
clearHighlights();
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
var assetName = move.isCastling ? 'castlingSquare' : 'highlightSquare';
var highlight = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
}));
var pos = getBoardPosition(move.row, move.col);
highlight.x = pos.x;
highlight.y = pos.y;
highlightedSquares.push(highlight);
}
}
function highlightSwappablePieces(swappablePieces) {
clearSwapHighlights();
for (var i = 0; i < swappablePieces.length; i++) {
var piece = swappablePieces[i];
var highlight = game.addChild(LK.getAsset('swapSquare', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
var pos = getBoardPosition(piece.boardRow, piece.boardCol);
highlight.x = pos.x;
highlight.y = pos.y;
swapHighlights.push(highlight);
}
}
function selectPiece(piece) {
if (selectedPiece) {
selectedPiece.removeChild(selectedPiece.selectionHighlight);
selectedPiece.selectionHighlight = null;
}
selectedPiece = piece;
if (piece) {
piece.selectionHighlight = piece.addChild(LK.getAsset('selectedSquare', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
var validMoves = piece.getValidMoves();
highlightMoves(validMoves);
// If selecting a Shah (king), also highlight swappable pieces
if (piece.pieceType === 'king') {
var swappablePieces = getSwappablePieces(piece);
highlightSwappablePieces(swappablePieces);
}
} else {
clearHighlights();
clearSwapHighlights();
}
}
function generateMoveNotation(piece, targetRow, targetCol, moveData, capturedPieces) {
var notation = '';
var pieceNotation = pieceToNotation(piece.pieceType);
var targetSquare = positionToAlgebraic(targetRow, targetCol);
// Handle special moves
if (moveData && moveData.isCastling) {
notation = targetCol > piece.boardCol ? 'O-O' : 'O-O-O';
} else {
// Add piece notation
notation += pieceNotation;
// Add capture notation
if (capturedPieces.length > 0) {
if (pieceNotation === '') {
// For pawn captures, add the file of origin
notation += positionToAlgebraic(piece.boardRow, piece.boardCol).charAt(0);
}
notation += 'x';
}
// Add destination square
notation += targetSquare;
}
return notation;
}
function makeMove(piece, targetRow, targetCol, moveData) {
var originalRow = piece.boardRow;
var originalCol = piece.boardCol;
// Check palace entry restrictions
if (isPalaceSquare(targetRow, targetCol)) {
if (!canEnterPalace(piece, targetRow, targetCol)) {
return; // Invalid move, cannot enter palace
}
}
var capturedPiece = null;
var capturedPieces = [];
// Handle special moves
if (moveData && moveData.isCastling) {
// Move the rook for castling
var rook = getPieceAt(originalRow, moveData.rookFromCol);
if (rook) {
rook.moveTo(originalRow, moveData.rookToCol);
}
// Play castle sound
LK.getSound('castle').play();
} else if (moveData && moveData.isEnPassant) {
// Capture the pawn for en passant
capturedPiece = getPieceAt(moveData.captureRow, moveData.captureCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
}
} else if (moveData && moveData.capturesInPath) {
// Handle siegeTower path captures
for (var k = 0; k < moveData.capturesInPath.length; k++) {
var capturePos = moveData.capturesInPath[k];
var pathPiece = getPieceAt(capturePos.row, capturePos.col);
if (pathPiece) {
capturedPieces.push(pathPiece);
}
}
} else {
// Handle remote capture for sea monster
if (moveData && moveData.isRemoteCapture) {
capturedPiece = getPieceAt(targetRow, targetCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
}
// Don't actually move the sea monster for remote captures
piece.hasMoved = true; // Mark as moved for game logic
// Skip the actual position change by returning early after cleanup
} else {
capturedPiece = getPieceAt(targetRow, targetCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
// Apply saray capture flag if needed
if (isSaray(targetRow, targetCol)) {
handleSarayCapture(piece, targetRow, targetCol);
}
}
}
}
// Remove all captured pieces
for (var k = 0; k < capturedPieces.length; k++) {
var capPiece = capturedPieces[k];
var index = pieces.indexOf(capPiece);
if (index > -1) {
pieces.splice(index, 1);
}
capPiece.destroy();
}
if (capturedPieces.length > 0) {
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Store move for en passant detection
lastMove = {
piece: piece,
fromRow: originalRow,
fromCol: originalCol,
toRow: targetRow,
toCol: targetCol
};
// Only move piece if it's not a remote capture
if (!moveData || !moveData.isRemoteCapture) {
piece.moveTo(targetRow, targetCol);
}
// Track movement for pawn's pawn
if (piece.pieceType === 'pawnsPawn') {
for (var mc = 0; mc < pawnsPawnMovementCount.length; mc++) {
if (pawnsPawnMovementCount[mc].piece === piece) {
pawnsPawnMovementCount[mc].moves++;
break;
}
}
}
// Check for vizier promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'queen' && !piece.isPromoted && !isTraditionalVersion) {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
piece.isPromoted = true;
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for minister promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'minister' && !piece.isPromoted && !isTraditionalVersion) {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
piece.isPromoted = true;
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x9932CC, 1000); // Flash purple
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the War Machine promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheWarMachine') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into siege tower
piece.pieceType = 'siegeTower';
// Change texture to siege tower
piece.removeChildAt(0); // Remove old texture
var siegeTowerGraphics = piece.attachAsset('siegeTower', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
siegeTowerGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x00FF00, 1000); // Flash green
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Camel Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'camelPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into camel
piece.pieceType = 'camel';
// Change texture to camel
piece.removeChildAt(0); // Remove old texture
var camelGraphics = piece.attachAsset('camel', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
camelGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFFF00, 1000); // Flash yellow
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Minister's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'ministersPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into minister (promoted only if not traditional version)
piece.pieceType = 'minister';
piece.isPromoted = !isTraditionalVersion;
// Change texture to minister
piece.removeChildAt(0); // Remove old texture
var ministerGraphics = piece.attachAsset('minister', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
ministerGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x9932CC, 1000); // Flash purple
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the Elephant promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheElephant') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into war elephant
piece.pieceType = 'warElephant';
// Change texture to war elephant
piece.removeChildAt(0); // Remove old texture
var warElephantGraphics = piece.attachAsset('warElephant', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
warElephantGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x8B4513, 1000); // Flash brown
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for King's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'kingsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into shahsade
piece.pieceType = 'shahsade';
// Change texture to shahsade
piece.removeChildAt(0); // Remove old texture
var shahsadeGraphics = piece.attachAsset('shahsade', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
shahsadeGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFF4500, 1000); // Flash orange red
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Vizier's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'viziersPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into vizier (promoted only if not traditional version)
piece.pieceType = 'queen';
piece.isPromoted = !isTraditionalVersion;
// Change texture to queen
piece.removeChildAt(0); // Remove old texture
var queenGraphics = piece.attachAsset('queen', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
queenGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Giraffe Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'giraffePawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into giraffe
piece.pieceType = 'giraffe';
// Change texture to giraffe
piece.removeChildAt(0); // Remove old texture
var giraffeGraphics = piece.attachAsset('giraffe', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
giraffeGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFA500, 1000); // Flash orange
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Knight Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'knightPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into knight
piece.pieceType = 'knight';
// Change texture to knight
piece.removeChildAt(0); // Remove old texture
var knightGraphics = piece.attachAsset('knight', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
knightGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x00CED1, 1000); // Flash dark turquoise
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Rook Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'rookPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into rook
piece.pieceType = 'rook';
// Change texture to rook
piece.removeChildAt(0); // Remove old texture
var rookGraphics = piece.attachAsset('rook', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
rookGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x0000FF, 1000); // Flash blue
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the Leader promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheLeader') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into bishop
piece.pieceType = 'bishop';
// Change texture to bishop
piece.removeChildAt(0); // Remove old texture
var bishopGraphics = piece.attachAsset('bishop', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
bishopGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x800080, 1000); // Flash purple
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Guard's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'guardsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into royalGuard
piece.pieceType = 'royalGuard';
// Change texture to royalGuard
piece.removeChildAt(0); // Remove old texture
var royalGuardGraphics = piece.attachAsset('royalGuard', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
royalGuardGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xCD5C5C, 1000); // Flash indian red
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the Bull promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheBull') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into bull
piece.pieceType = 'bull';
// Change texture to bull
piece.removeChildAt(0); // Remove old texture
var bullGraphics = piece.attachAsset('bull', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
bullGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xB8860B, 1000); // Flash dark goldenrod
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Lion's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'lionsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into lion
piece.pieceType = 'lion';
// Change texture to lion
piece.removeChildAt(0); // Remove old texture
var lionGraphics = piece.attachAsset('lion', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
lionGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for regular pawn promotion to pawn's pawn - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into pawn's pawn
piece.pieceType = 'pawnsPawn';
// Initialize movement count for this pawn's pawn
pawnsPawnMovementCount.push({
piece: piece,
moves: 0
});
// Change texture to pawn's pawn
piece.removeChildAt(0); // Remove old texture
var pawnsPawnGraphics = piece.attachAsset('pawnsPawn', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
pawnsPawnGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFF69B4, 1000); // Flash hot pink
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Find movement count for this piece
var moveCount = 0;
for (var mc = 0; mc < pawnsPawnMovementCount.length; mc++) {
if (pawnsPawnMovementCount[mc].piece === piece) {
moveCount = pawnsPawnMovementCount[mc].moves;
break;
}
}
// Must move at least 1 square to be promoted
if (moveCount >= 1) {
// Transform into adventurous king
piece.pieceType = 'adventurousKing';
// Change texture to adventurous king
piece.removeChildAt(0); // Remove old texture
var adventurousKingGraphics = piece.attachAsset('adventurousKing', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
adventurousKingGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x00FFFF, 1000); // Flash cyan
// Play promotion sound
LK.getSound('promotion').play();
}
}
}
// Removed adventurous king promotion to king when entering own saray
// Check for shahsade victory condition - capturing king with shahsade is immediate win
if (piece.pieceType === 'shahsade' && capturedPieces.length > 0) {
for (var k = 0; k < capturedPieces.length; k++) {
if (capturedPieces[k].pieceType === 'king') {
// Shahsade captured the king - immediate victory
LK.setScore(1);
LK.showYouWin();
return;
}
}
}
// Generate and record move notation
var notation = generateMoveNotation(piece, targetRow, targetCol, moveData, capturedPieces);
var moveNumber = Math.floor(moveHistory.length / 2) + 1;
var colorPrefix = currentPlayer === 0 ? moveNumber + '.' : '';
moveHistory.push(colorPrefix + notation);
// Update move history display only if visible
if (moveHistoryVisible) {
var displayText = 'Move History:\n';
for (var i = 0; i < moveHistory.length; i++) {
if (i % 2 === 0) {
displayText += moveHistory[i] + ' ';
} else {
displayText += moveHistory[i] + '\n';
}
}
moveHistoryText.setText(displayText);
}
// Check for draw offer if piece entered opponent's palace
if (isPalaceSquare(targetRow, targetCol)) {
if (offerDraw(piece)) {
return; // Game ended in draw
}
}
// Check if king must exit saray on next turn after move
checkIfKingMustExitNextTurn(currentPlayer);
currentPlayer = 1 - currentPlayer;
selectPiece(null);
// Check for victory conditions
checkGameEnd();
}
function isSquareAttacked(row, col, byColor) {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor !== byColor) {
continue;
}
var moves = piece.getRawMoves();
for (var j = 0; j < moves.length; j++) {
if (moves[j].row === row && moves[j].col === col) {
return true;
}
}
}
return false;
}
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;
}
// Check if the defending player has shahsade or adventurous king
var defendingHasShahsade = false;
var defendingHasAdventurousKing = false;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color) {
if (pieces[i].pieceType === 'shahsade') {
defendingHasShahsade = true;
}
if (pieces[i].pieceType === 'adventurousKing') {
defendingHasAdventurousKing = true;
}
}
}
// If defending player has no shahsade or adventurous king, king can be checked normally
if (!defendingHasShahsade && !defendingHasAdventurousKing) {
return isSquareAttacked(king.boardRow, king.boardCol, 1 - color);
}
// Check if the attacking player has a shahsade
var attackingColor = 1 - color;
var hasShahsade = false;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === attackingColor && pieces[i].pieceType === 'shahsade') {
hasShahsade = true;
break;
}
}
// If attacker has shahsade and it hasn't been promoted, they cannot check the king
if (hasShahsade && !shahsadePromoted[attackingColor]) {
return false;
}
return isSquareAttacked(king.boardRow, king.boardCol, 1 - color);
}
function isValidMoveConsideringCheck(piece, targetRow, targetCol) {
// Simulate the move
var originalRow = piece.boardRow;
var originalCol = piece.boardCol;
var capturedPiece = getPieceAt(targetRow, targetCol);
var capturedIndex = -1;
if (capturedPiece) {
capturedIndex = pieces.indexOf(capturedPiece);
pieces.splice(capturedIndex, 1);
}
piece.boardRow = targetRow;
piece.boardCol = targetCol;
// Convert adventurous king if needed after simulated move
convertAdventurousKingIfNeeded(piece.pieceColor);
var stillInCheck = isInCheck(piece.pieceColor);
var hasImmunity = hasImmunityPiece(piece.pieceColor);
// Restore the position
piece.boardRow = originalRow;
piece.boardCol = originalCol;
if (capturedPiece && capturedIndex > -1) {
pieces.splice(capturedIndex, 0, capturedPiece);
}
// Player can ignore check only if they have immunity piece
if (hasImmunity) {
return true;
}
// Otherwise check must be resolved
return !stillInCheck;
}
function hasValidMoves(color) {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor !== color) {
continue;
}
var moves = piece.getRawMoves();
for (var j = 0; j < moves.length; j++) {
if (isValidMoveConsideringCheck(piece, moves[j].row, moves[j].col)) {
return true;
}
}
}
return false;
}
function isSideLMove(fromRow, fromCol, toRow, toCol) {
var rowDiff = Math.abs(toRow - fromRow);
var colDiff = Math.abs(toCol - fromCol);
// Yan L: 2 yatay (col), 1 dikey (row)
if (rowDiff === 1 && colDiff === 2) {
return true; // Bu hareket yan L hareketidir
}
return false;
}
function hasRealKing(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color && pieces[i].pieceType === 'king') {
return true;
}
}
return false;
}
function hasAdventurousKing(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color && pieces[i].pieceType === 'adventurousKing') {
return true;
}
}
return false;
}
function hasShahsade(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color && pieces[i].pieceType === 'shahsade') {
return true;
}
}
return false;
}
function convertAdventurousKingIfNeeded(color) {
if (hasRealKing(color) || hasShahsade(color)) {
return;
}
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === color && p.pieceType === 'adventurousKing') {
p.pieceType = 'king';
// Change texture to king
p.removeChildAt(0); // Remove old texture
var kingGraphics = p.attachAsset('king', {
anchorX: 0.5,
anchorY: 0.5
});
if (p.pieceColor === 1) {
kingGraphics.tint = 0x333333;
}
// Visual feedback for conversion
LK.effects.flashObject(p, 0xFFD700, 1000); // Flash gold
console.log('AdventurousKing converted to KING at (' + r + ',' + c + ')');
return;
}
}
}
}
function checkGameEnd() {
// Convert adventurous kings if needed for both players
convertAdventurousKingIfNeeded(0);
convertAdventurousKingIfNeeded(1);
var whiteKing = findKing(0);
var blackKing = findKing(1);
// Check if a player who brought shahsade into play loses their king
if (!whiteKing) {
// White king is captured - check if white has shahsade
var whiteShahsade = null;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === 0 && pieces[i].pieceType === 'shahsade') {
whiteShahsade = pieces[i];
break;
}
}
if (whiteShahsade) {
// Promote shahsade to new shah (king)
whiteShahsade.pieceType = 'king';
// Mark that white's shahsade has been promoted
shahsadePromoted[0] = true;
// Change texture to king
whiteShahsade.removeChildAt(0); // Remove old texture
var kingGraphics = whiteShahsade.attachAsset('king', {
anchorX: 0.5,
anchorY: 0.5
});
if (whiteShahsade.pieceColor === 1) {
kingGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(whiteShahsade, 0xFFD700, 1000); // Flash gold
// Check if player is in checkmate after promotion
if (isInCheck(0) && !hasValidMoves(0)) {
LK.setScore(1);
LK.showGameOver();
return;
}
// Game continues, don't end
return;
} else {
LK.setScore(1);
LK.showGameOver();
return;
}
}
if (!blackKing) {
// Black king is captured - check if black has shahsade
var blackShahsade = null;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === 1 && pieces[i].pieceType === 'shahsade') {
blackShahsade = pieces[i];
break;
}
}
if (blackShahsade) {
// Promote shahsade to new shah (king)
blackShahsade.pieceType = 'king';
// Mark that black's shahsade has been promoted
shahsadePromoted[1] = true;
// Change texture to king
blackShahsade.removeChildAt(0); // Remove old texture
var kingGraphics = blackShahsade.attachAsset('king', {
anchorX: 0.5,
anchorY: 0.5
});
if (blackShahsade.pieceColor === 1) {
kingGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(blackShahsade, 0xFFD700, 1000); // Flash gold
// Check if player is in checkmate after promotion
if (isInCheck(1) && !hasValidMoves(1)) {
LK.setScore(1);
LK.showGameOver();
return;
}
// Game continues, don't end
return;
} else {
LK.setScore(1);
LK.showGameOver();
return;
}
}
var inCheck = isInCheck(currentPlayer);
var hasValid = hasValidMoves(currentPlayer);
// Check for checkmate using new function that respects check privileges
if (isCheckmate(currentPlayer)) {
// Checkmate
LK.getSound('checkmate').play();
LK.setScore(1);
LK.showGameOver();
} else if (!hasValid) {
// Stalemate
LK.setScore(0);
LK.showGameOver();
}
}
function createMenu() {
// Create menu container
menuContainer = game.addChild(new Container());
menuContainer.x = 2048 / 2;
menuContainer.y = 2732 / 2;
// Create menu background with Timur portrait
var menuBg = menuContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var titleBox = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 1.0
}));
titleBox.x = 0;
titleBox.y = -1100;
// Create title text
var titleText = new Text2('TAMERLANE CHESS', {
size: 120,
fill: 0x808080,
font: "Algerian,Impact,Arial Black,Tahoma"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -1100;
menuContainer.addChild(titleText);
// Create start button
var startButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
startButton.x = -800;
startButton.y = 550;
var startButtonText = new Text2('START GAME', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
font: "Algerian,Impact,Arial Black,Tahoma"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = 0;
startButton.addChild(startButtonText);
// Create rules button
var rulesButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
rulesButton.x = -800;
rulesButton.y = 700;
var rulesButtonText = new Text2('HOW TO PLAY', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
font: "Algerian,Impact,Arial Black,Tahoma"
});
rulesButtonText.anchor.set(0.5, 0.5);
rulesButtonText.x = 0;
rulesButtonText.y = 0;
rulesButton.addChild(rulesButtonText);
// Add click handlers
startButton.down = function () {
hideMenu();
showVersionSelection();
};
rulesButton.down = function () {
showRulesDialog();
};
// Add hover effects
startButton.move = function () {
startButton.removeChildAt(0); // Remove old background
var hoverBg = startButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
startButton.addChild(startButtonText); // Re-add text on top
};
rulesButton.move = function () {
rulesButton.removeChildAt(0); // Remove old background
var hoverBg = rulesButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
rulesButton.addChild(rulesButtonText); // Re-add text on top
};
}
function hideMenu() {
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
menuVisible = false;
}
function showRulesDialog() {
// Create rules dialog background with Timur portrait
var rulesBg = game.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.8,
alpha: 0.9
}));
rulesBg.x = 2048 / 2;
rulesBg.y = 2732 / 2;
// Create rules text
var rulesText = new Text2('Click anywhere to close', {
size: 45,
fill: 0xFFFFFF
});
rulesText.anchor.set(0.5, 0.5);
rulesText.x = 2048 / 2;
rulesText.y = 2732 / 2;
game.addChild(rulesText);
// Add close handler
rulesBg.down = function () {
rulesBg.destroy();
rulesText.destroy();
};
rulesText.down = function () {
rulesBg.destroy();
rulesText.destroy();
};
}
function initializeGame() {
createBoard();
createPieces();
gameStarted = true;
turnText.visible = true;
moveHistoryTab.visible = true;
}
// Player turn indicator
var turnText = new Text2('White to move', {
size: 80,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.visible = false; // Hide until game starts
LK.gui.top.addChild(turnText);
// Move history display
var moveHistory = [];
var moveHistoryVisible = false;
var moveHistoryTab = new Text2('Move History', {
size: 50,
fill: 0xFFFFFF
});
moveHistoryTab.anchor.set(0, 0);
moveHistoryTab.x = 50;
moveHistoryTab.y = 150;
moveHistoryTab.visible = false; // Hide until game starts
game.addChild(moveHistoryTab);
var moveHistoryText = new Text2('', {
size: 40,
fill: 0xFFFFFF
});
moveHistoryText.anchor.set(0, 0);
moveHistoryText.x = 50;
moveHistoryText.y = 200;
moveHistoryText.visible = false;
game.addChild(moveHistoryText);
game.down = function (x, y, obj) {
if (!gameStarted || menuVisible || versionSelectionVisible || colorSelectionVisible || aiSelectionVisible) {
return;
}
// Prevent human interaction when it's AI's turn
if (playWithAI && currentPlayer === aiColor) {
return;
}
// Check if move history tab was clicked
if (x >= moveHistoryTab.x && x <= moveHistoryTab.x + 300 && y >= moveHistoryTab.y && y <= moveHistoryTab.y + 60) {
moveHistoryVisible = !moveHistoryVisible;
moveHistoryText.visible = moveHistoryVisible;
if (moveHistoryVisible) {
// Update display when showing
var displayText = 'Move History:\n';
for (var i = 0; i < moveHistory.length; i++) {
if (i % 2 === 0) {
displayText += moveHistory[i] + ' ';
} else {
displayText += moveHistory[i] + '\n';
}
}
moveHistoryText.setText(displayText);
}
return;
}
var coords = getBoardCoordinates(x, y);
if (!isValidSquare(coords.row, coords.col)) {
return;
}
var clickedPiece = getPieceAt(coords.row, coords.col);
if (selectedPiece) {
var validMoves = selectedPiece.getValidMoves();
var isValidMove = false;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === coords.row && validMoves[i].col === coords.col) {
isValidMove = true;
break;
}
}
if (isValidMove) {
var moveData = null;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === coords.row && validMoves[i].col === coords.col) {
moveData = validMoves[i];
break;
}
}
makeMove(selectedPiece, coords.row, coords.col, moveData);
return;
}
}
if (clickedPiece && clickedPiece.pieceColor === currentPlayer) {
// Check if this is a Shah position swap
if (selectedPiece && selectedPiece.pieceType === 'king' && clickedPiece !== selectedPiece) {
var swappablePieces = getSwappablePieces(selectedPiece);
var canSwap = false;
for (var i = 0; i < swappablePieces.length; i++) {
if (swappablePieces[i] === clickedPiece) {
canSwap = true;
break;
}
}
if (canSwap) {
performPositionSwap(selectedPiece, clickedPiece);
return;
}
}
selectPiece(clickedPiece);
} else {
selectPiece(null);
}
};
game.update = function () {
if (!gameStarted && !menuVisible && !versionSelectionVisible && !colorSelectionVisible && !aiSelectionVisible) {
initializeGame();
} else if (menuVisible && !menuContainer) {
createMenu();
} else if (versionSelectionVisible && !versionContainer) {
showVersionSelection();
} else if (colorSelectionVisible && !colorContainer) {
showColorSelection();
} else if (aiSelectionVisible && !aiContainer) {
showAISelection();
}
// Try teleporting pawn of pawns for current player
tryTeleportPawnOfPawns(currentPlayer);
// Enforce saray exit if required
enforceSarayExit(currentPlayer);
var playerText = currentPlayer === 0 ? 'White to move' : 'Black to move';
if (isInCheck(currentPlayer)) {
playerText += ' (Check!)';
}
if (playWithAI && currentPlayer === aiColor) {
playerText += ' (AI thinking...)';
}
turnText.setText(playerText);
// AI move logic
if (playWithAI && gameStarted && currentPlayer === aiColor) {
makeAIMove();
}
};
function enforceSarayExit(playerColor) {
// Check if player is mated due to saray exit requirement
if (checkForcedSarayExitOrMate(playerColor)) {
// Player is mated - end game
LK.setScore(1);
LK.showGameOver();
return;
}
for (var row = 0; row < BOARD_HEIGHT; row++) {
for (var col = 0; col < BOARD_WIDTH; col++) {
var p = getPieceAt(row, col);
if (p && p.pieceColor === playerColor && mustMoveFirst(p, row, col)) {
// Highlight the piece that must exit saray
if (selectedPiece !== p) {
selectPiece(p);
}
return;
}
}
}
}
// List of types the pawn_of_pawns cannot threaten
var EXCLUDED_TYPES = ['camelPawn', 'giraffePawn', 'guardsPawn', 'kingsPawn', 'knightPawn', 'lionsPawn', 'ministersPawn', 'pawnOfTheBull', 'pawnOfTheElephant', 'pawnOfTheLeader', 'pawn', 'pawnOfTheWarMachine', 'viziersPawn', 'king'];
function tryTeleportPawnOfPawns(color) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceType === 'pawnsPawn' && p.pieceColor === color && !p.hasTeleported) {
for (var tr = 0; tr < BOARD_HEIGHT; tr++) {
for (var tc = 0; tc < BOARD_WIDTH; tc++) {
if (!isValidSquare(tr, tc)) {
continue;
} // Skip invalid squares
if (getPieceAt(tr, tc)) {
continue;
} // Square must be empty
if (isSeaSquare(tr, tc)) {
continue;
} // Skip sea squares - pawn's pawn cannot teleport to sea
// Check if target square is a promotion square - pawn's pawn cannot teleport to promotion squares
var opponentPromotionRow = color === 0 ? 1 : BOARD_HEIGHT - 2;
if (tr === opponentPromotionRow) {
continue;
} // Skip promotion squares
var threats = countValidThreats(tr, tc, color);
var blocksImmobilizedEnemy = threatensBlockedEnemy(tr, tc, color);
if (threats >= 2 || blocksImmobilizedEnemy) {
// Teleport the pawn of pawns
p.boardRow = tr;
p.boardCol = tc;
p.hasTeleported = true;
var pos = getBoardPosition(tr, tc);
tween(p, {
x: pos.x,
y: pos.y
}, {
duration: 300
});
console.log('Pawn of Pawns TELEPORTED to (' + tr + ',' + tc + ')');
return;
}
}
}
}
}
}
}
function countValidThreats(row, col, color) {
var count = 0;
var dirs = [[-1, -1], [-1, 1], [1, -1], [1, 1]]; // Diagonals
for (var d = 0; d < dirs.length; d++) {
var dir = dirs[d];
var r = row + dir[0];
var c = col + dir[1];
if (isValidSquare(r, c)) {
var enemy = getPieceAt(r, c);
if (enemy && enemy.pieceColor !== color && EXCLUDED_TYPES.indexOf(enemy.pieceType) === -1) {
// Additional check: pawn of pawns cannot teleport to threaten the king
if (enemy.pieceType === 'king') {
return 0; // Prevent teleportation if it would threaten the king
}
count++;
}
}
}
return count;
}
function threatensBlockedEnemy(row, col, color) {
var dirs = [[-1, -1], [-1, 1], [1, -1], [1, 1]]; // Diagonals
for (var d = 0; d < dirs.length; d++) {
var dir = dirs[d];
var r = row + dir[0];
var c = col + dir[1];
if (isValidSquare(r, c)) {
var enemy = getPieceAt(r, c);
if (enemy && enemy.pieceColor !== color && EXCLUDED_TYPES.indexOf(enemy.pieceType) === -1) {
// Additional check: pawn of pawns cannot teleport to threaten the king
if (enemy.pieceType === 'king') {
return false; // Prevent teleportation if it would threaten the king
}
if (!hasLegalMovesForPiece(enemy, r, c)) {
return true;
}
}
}
}
return false;
}
function hasLegalMovesForPiece(piece, row, col) {
var moves = piece.getRawMoves();
for (var i = 0; i < moves.length; i++) {
if (isValidMoveConsideringCheck(piece, moves[i].row, moves[i].col)) {
return true;
}
}
return false;
}
function hasImmunityPiece(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color) {
if (pieces[i].pieceType === 'adventurousKing' || pieces[i].pieceType === 'shahsade') {
return true;
}
}
}
return false;
}
function isPieceUnderThreat(piece) {
return isSquareAttacked(piece.boardRow, piece.boardCol, 1 - piece.pieceColor);
}
function hasCheckPrivilege(color) {
return hasAdventurousKing(color) || hasShahsade(color);
}
function getSwappablePieces(shahPiece) {
var swappablePieces = [];
// Check if Shah position swap has already been used
if (shahPositionSwapUsed[shahPiece.pieceColor]) {
return swappablePieces;
}
// Shah must not be under threat
if (isPieceUnderThreat(shahPiece)) {
return swappablePieces;
}
// Check if king is on promotion square
var isOnPromotionSquare = false;
var opponentPromotionRow = shahPiece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (shahPiece.boardRow === opponentPromotionRow) {
isOnPromotionSquare = true;
}
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
// Must be same color but not the Shah itself
if (piece.pieceColor === shahPiece.pieceColor && piece !== shahPiece) {
// If king is on promotion square, exclude all pawn types from swapping
if (isOnPromotionSquare) {
var pawnTypes = ['pawn', 'pawnOfTheWarMachine', 'camelPawn', 'ministersPawn', 'pawnOfTheElephant', 'kingsPawn', 'viziersPawn', 'giraffePawn', 'knightPawn', 'rookPawn', 'pawnOfTheLeader', 'guardsPawn', 'pawnOfTheBull', 'lionsPawn', 'pawnsPawn'];
if (pawnTypes.indexOf(piece.pieceType) !== -1) {
continue; // Skip pawn pieces when king is on promotion square
}
}
// Piece must not be under threat
if (!isPieceUnderThreat(piece)) {
swappablePieces.push(piece);
}
}
}
return swappablePieces;
}
function isCheckmate(color) {
if (hasImmunityPiece(color)) {
console.log(color === 0 ? 'White' : 'Black' + ' has immunity — checkmate ignored.');
return false;
}
return isInCheck(color) && !hasValidMoves(color);
}
function isPalaceSquare(row, col) {
return row === 2 && col === 0 || row === 9 && col === 12;
}
function isSaray(row, col) {
return row === 2 && col === 0 || row === 9 && col === 12;
}
function isInOwnSaray(piece, row, col) {
return piece.pieceColor === 0 && row === 9 && col === 12 || piece.pieceColor === 1 && row === 2 && col === 0;
}
function promoteAdventurousKingToKing(row, col) {
// Removed adventurous king promotion to king when entering own saray
}
function mustExitSarayThisTurn(playerColor) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === playerColor && p.mustExitSaray && isInOwnSaray(p, r, c)) {
return true;
}
}
}
return false;
}
function canEscapeFromSaray(playerColor) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === playerColor && p.mustExitSaray && isInOwnSaray(p, r, c)) {
// Check 8 surrounding squares (like king movement)
var dr = [-1, -1, -1, 0, 1, 1, 1, 0];
var dc = [-1, 0, 1, 1, 1, 0, -1, -1];
for (var i = 0; i < 8; i++) {
var nr = r + dr[i];
var nc = c + dc[i];
if (isValidSquare(nr, nc) && !getPieceAt(nr, nc)) {
return true; // Found an empty escape square
}
}
return false; // Surrounded, no escape
}
}
}
return true; // No forced exit kings found
}
function checkForcedSarayExitOrMate(playerColor) {
if (mustExitSarayThisTurn(playerColor)) {
if (canEscapeFromSaray(playerColor)) {
console.log((playerColor === 0 ? 'White' : 'Black') + ' must move King out of saray this turn.');
return false; // Not mated, but must move
} else {
console.log((playerColor === 0 ? 'White' : 'Black') + ' cannot escape saray — checkmate.');
return true; // Mated
}
}
return false; // No forced exits pending
}
function checkIfKingMustExitNextTurn(playerColor) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === playerColor && p.pieceType === 'king' && isInOwnSaray(p, r, c)) {
p.mustExitSaray = true;
console.log((playerColor === 0 ? 'White' : 'Black') + "'s king must exit saray on next turn.");
}
}
}
}
function handleSarayCapture(attacker, row, col) {
if (isSaray(row, col)) {
attacker.mustExitSaray = true;
}
}
function mustMoveFirst(piece, row, col) {
if (!piece || !piece.mustExitSaray) {
return false;
}
if (isSaray(row, col)) {
return true; // still inside → must move out
} else {
piece.mustExitSaray = false; // already out → clear flag
return false;
}
}
function getPlayerPalace(color) {
return color === 1 ? {
row: 2,
col: 0
} : {
row: 9,
col: 12
};
}
function isOpponentPalace(row, col, color) {
if (color === 0) {
return row === 2 && col === 0; // White player, black palace
} else {
return row === 9 && col === 12; // Black player, white palace
}
}
function canEnterPalace(piece, targetRow, targetCol) {
// Coordinates of palace squares
var blackPalace = {
row: 2,
col: 0
}; // Black Palace location
var whitePalace = {
row: 9,
col: 12
}; // White Palace location
if (!piece) {
return false;
}
var type = piece.pieceType;
var color = piece.pieceColor === 0 ? 'white' : 'black';
var isWhiteSaray = targetRow === whitePalace.row && targetCol === whitePalace.col;
var isBlackSaray = targetRow === blackPalace.row && targetCol === blackPalace.col;
var target = getPieceAt(targetRow, targetCol);
var isCapturing = target && target.pieceColor !== piece.pieceColor;
// Normal allowed entry
if (type === 'adventurousKing' || isBlackSaray && color === 'white' && (type === 'king' || type === 'shahsade') || isWhiteSaray && color === 'black' && (type === 'king' || type === 'shahsade')) {
return true;
}
// Special rule: capture king/shahsade/adventurousKing in saray if under threat
if (isCapturing && target && (target.pieceType === 'king' || target.pieceType === 'shahsade' || target.pieceType === 'adventurousKing') && isSaray(targetRow, targetCol)) {
return true;
}
return !isSaray(targetRow, targetCol); // deny entry to saray unless allowed
}
function isPieceInPalace(piece) {
return isPalaceSquare(piece.boardRow, piece.boardCol);
}
var drawOfferPending = false;
var drawOfferPiece = null;
var versionSelectionVisible = false;
var versionContainer = null;
var isTraditionalVersion = false;
var colorSelectionVisible = false;
var colorContainer = null;
var playerColor = 0; // 0 = white, 1 = black
var aiSelectionVisible = false;
var aiContainer = null;
var playWithAI = false;
var aiColor = 1; // AI plays opposite color to player
function showVersionSelection() {
versionSelectionVisible = true;
// Create version selection container
versionContainer = game.addChild(new Container());
versionContainer.x = 2048 / 2;
versionContainer.y = 2732 / 2;
// Create background with Timur portrait
var versionBg = versionContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var versionTitleBox = versionContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.0
}));
versionTitleBox.x = 0;
versionTitleBox.y = -200;
// Create title text
var versionTitleText = new Text2('CHOOSE VERSION', {
size: 100,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
versionTitleText.anchor.set(0.5, 0.5);
versionTitleText.x = 0;
versionTitleText.y = -200;
versionContainer.addChild(versionTitleText);
// Create Traditional Version button
var traditionalButton = versionContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
traditionalButton.x = 0;
traditionalButton.y = -50;
var traditionalButtonText = new Text2('TRADITIONAL VERSION', {
size: 50,
fill: 0xFFFFFF
});
traditionalButtonText.anchor.set(0.5, 0.5);
traditionalButtonText.x = 0;
traditionalButtonText.y = 0;
traditionalButton.addChild(traditionalButtonText);
// Create Modern Version button
var modernButton = versionContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
modernButton.x = 0;
modernButton.y = 100;
var modernButtonText = new Text2('MODERN VERSION', {
size: 50,
fill: 0xFFFFFF
});
modernButtonText.anchor.set(0.5, 0.5);
modernButtonText.x = 0;
modernButtonText.y = 0;
modernButton.addChild(modernButtonText);
// Add click handlers
traditionalButton.down = function () {
isTraditionalVersion = true;
hideVersionSelection();
showColorSelection();
};
modernButton.down = function () {
isTraditionalVersion = false;
hideVersionSelection();
showColorSelection();
};
// Add hover effects
traditionalButton.move = function () {
traditionalButton.removeChildAt(0); // Remove old background
var hoverBg = traditionalButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
traditionalButton.addChild(traditionalButtonText); // Re-add text on top
};
modernButton.move = function () {
modernButton.removeChildAt(0); // Remove old background
var hoverBg = modernButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
modernButton.addChild(modernButtonText); // Re-add text on top
};
}
function hideVersionSelection() {
if (versionContainer) {
versionContainer.destroy();
versionContainer = null;
}
versionSelectionVisible = false;
}
function showColorSelection() {
colorSelectionVisible = true;
// Create color selection container
colorContainer = game.addChild(new Container());
colorContainer.x = 2048 / 2;
colorContainer.y = 2732 / 2;
// Create background with Timur portrait
var colorBg = colorContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var colorTitleBox = colorContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.3,
scaleY: 1.0
}));
colorTitleBox.x = 0;
colorTitleBox.y = -200;
// Create title text
var colorTitleText = new Text2('CHOOSE YOUR COLOR', {
size: 100,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
colorTitleText.anchor.set(0.5, 0.5);
colorTitleText.x = 0;
colorTitleText.y = -200;
colorContainer.addChild(colorTitleText);
// Create White button
var whiteButton = colorContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
whiteButton.x = 0;
whiteButton.y = -50;
var whiteButtonText = new Text2('PLAY AS WHITE', {
size: 50,
fill: 0xFFFFFF
});
whiteButtonText.anchor.set(0.5, 0.5);
whiteButtonText.x = 0;
whiteButtonText.y = 0;
whiteButton.addChild(whiteButtonText);
// Create Black button
var blackButton = colorContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
blackButton.x = 0;
blackButton.y = 100;
var blackButtonText = new Text2('PLAY AS BLACK', {
size: 50,
fill: 0xFFFFFF
});
blackButtonText.anchor.set(0.5, 0.5);
blackButtonText.x = 0;
blackButtonText.y = 0;
blackButton.addChild(blackButtonText);
// Add click handlers
whiteButton.down = function () {
playerColor = 0;
currentPlayer = 0; // White starts first
hideColorSelection();
showAISelection();
};
blackButton.down = function () {
playerColor = 1;
currentPlayer = 0; // White still starts first, but player chose black
hideColorSelection();
showAISelection();
};
// Add hover effects
whiteButton.move = function () {
whiteButton.removeChildAt(0); // Remove old background
var hoverBg = whiteButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
whiteButton.addChild(whiteButtonText); // Re-add text on top
};
blackButton.move = function () {
blackButton.removeChildAt(0); // Remove old background
var hoverBg = blackButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
blackButton.addChild(blackButtonText); // Re-add text on top
};
}
function hideColorSelection() {
if (colorContainer) {
colorContainer.destroy();
colorContainer = null;
}
colorSelectionVisible = false;
}
function showAISelection() {
aiSelectionVisible = true;
aiColor = 1 - playerColor; // AI plays opposite color to player
// Create AI selection container
aiContainer = game.addChild(new Container());
aiContainer.x = 2048 / 2;
aiContainer.y = 2732 / 2;
// Create background with Timur portrait
var aiBg = aiContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var aiTitleBox = aiContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 1.0
}));
aiTitleBox.x = 0;
aiTitleBox.y = -200;
// Create title text
var aiTitleText = new Text2('CHOOSE OPPONENT', {
size: 100,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
aiTitleText.anchor.set(0.5, 0.5);
aiTitleText.x = 0;
aiTitleText.y = -200;
aiContainer.addChild(aiTitleText);
// Create AI button
var aiButton = aiContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
aiButton.x = 0;
aiButton.y = -50;
var aiButtonText = new Text2('PLAY VS AI', {
size: 50,
fill: 0xFFFFFF
});
aiButtonText.anchor.set(0.5, 0.5);
aiButtonText.x = 0;
aiButtonText.y = 0;
aiButton.addChild(aiButtonText);
// Create Human button
var humanButton = aiContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
humanButton.x = 0;
humanButton.y = 100;
var humanButtonText = new Text2('PLAY ALONE', {
size: 50,
fill: 0xFFFFFF
});
humanButtonText.anchor.set(0.5, 0.5);
humanButtonText.x = 0;
humanButtonText.y = 0;
humanButton.addChild(humanButtonText);
// Add click handlers
aiButton.down = function () {
playWithAI = true;
hideAISelection();
initializeGame();
};
humanButton.down = function () {
playWithAI = false;
hideAISelection();
initializeGame();
};
// Add hover effects
aiButton.move = function () {
aiButton.removeChildAt(0); // Remove old background
var hoverBg = aiButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
aiButton.addChild(aiButtonText); // Re-add text on top
};
humanButton.move = function () {
humanButton.removeChildAt(0); // Remove old background
var hoverBg = humanButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
humanButton.addChild(humanButtonText); // Re-add text on top
};
}
function hideAISelection() {
if (aiContainer) {
aiContainer.destroy();
aiContainer = null;
}
aiSelectionVisible = false;
}
function offerDraw(piece) {
// Check if piece is in opponent's palace
if (isOpponentPalace(piece.boardRow, piece.boardCol, piece.pieceColor)) {
var playerName = piece.pieceColor === 0 ? 'White' : 'Black';
drawOfferPending = true;
drawOfferPiece = piece;
// Create draw offer dialog
showDrawOfferDialog(playerName);
return true; // Game is paused for draw offer
}
return false;
}
function showDrawOfferDialog(playerName) {
// Create dialog background
var dialogBg = game.addChild(LK.getAsset('darkSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 3,
alpha: 0.9
}));
dialogBg.x = 2048 / 2;
dialogBg.y = 2732 / 2;
// Create dialog text
var dialogText = new Text2(playerName + ' offers a draw.\nDo you accept?', {
size: 60,
fill: 0xFFFFFF
});
dialogText.anchor.set(0.5, 0.5);
dialogText.x = 2048 / 2;
dialogText.y = 2732 / 2 - 100;
game.addChild(dialogText);
// Create Yes button
var yesButton = game.addChild(LK.getAsset('lightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1
}));
yesButton.x = 2048 / 2 - 200;
yesButton.y = 2732 / 2 + 100;
var yesText = new Text2('Yes', {
size: 50,
fill: 0x000000
});
yesText.anchor.set(0.5, 0.5);
yesText.x = yesButton.x;
yesText.y = yesButton.y;
game.addChild(yesText);
// Create No button
var noButton = game.addChild(LK.getAsset('lightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1
}));
noButton.x = 2048 / 2 + 200;
noButton.y = 2732 / 2 + 100;
var noText = new Text2('No', {
size: 50,
fill: 0x000000
});
noText.anchor.set(0.5, 0.5);
noText.x = noButton.x;
noText.y = noButton.y;
game.addChild(noText);
// Add click handlers
yesButton.down = function () {
// Accept draw
LK.setScore(0); // Draw
LK.showGameOver();
};
noButton.down = function () {
// Decline draw - clean up dialog and continue game
dialogBg.destroy();
dialogText.destroy();
yesButton.destroy();
yesText.destroy();
noButton.destroy();
noText.destroy();
drawOfferPending = false;
drawOfferPiece = null;
};
}
function performPositionSwap(shahPiece, targetPiece) {
// Store original positions
var shahRow = shahPiece.boardRow;
var shahCol = shahPiece.boardCol;
var targetRow = targetPiece.boardRow;
var targetCol = targetPiece.boardCol;
// Swap positions
shahPiece.boardRow = targetRow;
shahPiece.boardCol = targetCol;
targetPiece.boardRow = shahRow;
targetPiece.boardCol = shahCol;
// Animate the swap
var shahPos = getBoardPosition(targetRow, targetCol);
var targetPos = getBoardPosition(shahRow, shahCol);
tween(shahPiece, {
x: shahPos.x,
y: shahPos.y
}, {
duration: 300
});
tween(targetPiece, {
x: targetPos.x,
y: targetPos.y
}, {
duration: 300
});
// Mark that position swap has been used
shahPositionSwapUsed[shahPiece.pieceColor] = true;
// Record position swap in move history
var notation = 'K-' + pieceToNotation(targetPiece.pieceType) + ' swap';
var moveNumber = Math.floor(moveHistory.length / 2) + 1;
var colorPrefix = currentPlayer === 0 ? moveNumber + '.' : '';
moveHistory.push(colorPrefix + notation);
// Update move history display only if visible
if (moveHistoryVisible) {
var displayText = 'Move History:\n';
for (var i = 0; i < moveHistory.length; i++) {
if (i % 2 === 0) {
displayText += moveHistory[i] + ' ';
} else {
displayText += moveHistory[i] + '\n';
}
}
moveHistoryText.setText(displayText);
}
// Play move sound
LK.getSound('move').play();
// Change turn
currentPlayer = 1 - currentPlayer;
selectPiece(null);
clearSwapHighlights();
} /****
* 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; // 0 = white, 1 = black
self.boardRow = row;
self.boardCol = col;
self.hasMoved = false;
self.isPromoted = false; // Track permanent promotion status for vizier
self.hasTeleported = false; // Track if pawn's pawn has used teleportation
self.mustExitSaray = false; // Track if piece must exit saray after capture
var pieceGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
if (color === 1) {
pieceGraphics.tint = 0x333333;
}
self.moveTo = function (newRow, newCol) {
self.boardRow = newRow;
self.boardCol = newCol;
self.hasMoved = true;
var boardPos = getBoardPosition(newRow, newCol);
tween(self, {
x: boardPos.x,
y: boardPos.y
}, {
duration: 300
});
};
self.getRawMoves = function () {
var moves = [];
var directions = [];
switch (self.pieceType) {
case 'pawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'rook':
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
for (var dist = 1; dist < 11; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
break;
case 'bishop':
// Bishop can move one square orthogonally (row/column only) - not allowed in traditional version
if (!isTraditionalVersion) {
var oneSquareOrthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
// Add one square moves in orthogonal directions only
for (var i = 0; i < oneSquareOrthogonalDirections.length; i++) {
var dir = oneSquareOrthogonalDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// Bishop can also move as many squares as wanted diagonally (minimum 2) without jumping over friendly pieces
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
for (var dist = 2; dist < 11; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
// Check if path is blocked by any piece (friendly or enemy)
var pathBlocked = false;
for (var checkDist = 1; checkDist < dist; checkDist++) {
var checkRow = self.boardRow + dir.dr * checkDist;
var checkCol = self.boardCol + dir.dc * checkDist;
var pathPiece = getPieceAt(checkRow, checkCol);
if (pathPiece) {
pathBlocked = true;
break;
}
}
if (pathBlocked) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
break;
case 'queen':
// Vizier: restricted to one square, enhanced to eight squares when promoted
// Diagonal movement is closed at the beginning (only orthogonal moves allowed)
// When promoted, both orthogonal and diagonal moves are allowed
if (self.isPromoted) {
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
} else {
// Only orthogonal moves at the beginning (diagonals closed)
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
}
var maxDistance = self.isPromoted ? 8 : 1;
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
for (var dist = 1; dist <= maxDistance; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
break;
case 'king':
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'knight':
var knightMoves = [{
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}, {
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
}, {
dr: 1,
dc: 2
}, {
dr: 1,
dc: -2
}, {
dr: -1,
dc: 2
}, {
dr: -1,
dc: -2
}];
for (var i = 0; i < knightMoves.length; i++) {
var move = knightMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'warElephant':
if (isTraditionalVersion) {
// Traditional version: elephant can only jump 2 squares diagonally
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var dist = 2; // Only 2 squares in traditional version
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
continue;
}
// Elephant can jump over pieces
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
} else {
// Modern version: Moves 1 or 2 steps row, column, and diagonal with jumping ability
// Elephant cannot be stopped by any pieces (friendly or enemy)
directions = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
for (var dist = 1; dist <= 2; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
// Elephant ignores all pieces and can move through them
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
// Continue moving even if there's a piece - elephant cannot be stopped
}
}
}
break;
case 'siegeTower':
if (isTraditionalVersion) {
// Traditional version: siegeTower can only skip 2 squares in columns and rows
var orthogonalDirections = [{
dr: 2,
dc: 0
}, {
dr: -2,
dc: 0
}, {
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}];
for (var i = 0; i < orthogonalDirections.length; i++) {
var orthDir = orthogonalDirections[i];
var newRow = self.boardRow + orthDir.dr;
var newCol = self.boardCol + orthDir.dc;
if (!isValidSquare(newRow, newCol)) {
continue;
}
// Siege tower can jump over pieces in traditional version
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
} else {
// Modern version: Must move 1 square in row/column, then 1 square diagonally
// Captures enemy pieces in its path
// siegeTower cannot be stopped by enemies
var orthogonalDirections = [{
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}];
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
// Try L-shaped moves: orthogonal then diagonal
for (var i = 0; i < orthogonalDirections.length; i++) {
var orthDir = orthogonalDirections[i];
var firstRow = self.boardRow + orthDir.dr;
var firstCol = self.boardCol + orthDir.dc;
if (!isValidSquare(firstRow, firstCol)) {
continue;
}
var firstPiece = getPieceAt(firstRow, firstCol);
// siegeTower cannot be stopped by enemies, but friendly pieces still block
if (firstPiece && firstPiece.pieceColor === self.pieceColor) {
// Can't move through friendly piece, skip this direction
continue;
}
// Try diagonal moves from the orthogonal position
for (var j = 0; j < diagonalDirections.length; j++) {
var diagDir = diagonalDirections[j];
var finalRow = firstRow + diagDir.dr;
var finalCol = firstCol + diagDir.dc;
if (!isValidSquare(finalRow, finalCol)) {
continue;
}
var finalPiece = getPieceAt(finalRow, finalCol);
if (!finalPiece || finalPiece.pieceColor !== self.pieceColor) {
var moveData = {
row: finalRow,
col: finalCol,
capturesInPath: []
};
// Add captured pieces in path
if (firstPiece && firstPiece.pieceColor !== self.pieceColor) {
moveData.capturesInPath.push({
row: firstRow,
col: firstCol
});
}
if (finalPiece && finalPiece.pieceColor !== self.pieceColor) {
moveData.capturesInPath.push({
row: finalRow,
col: finalCol
});
}
moves.push(moveData);
}
}
}
// If friendly piece in orthogonal path, allow simple orthogonal move
for (var i = 0; i < orthogonalDirections.length; i++) {
var orthDir = orthogonalDirections[i];
var newRow = self.boardRow + orthDir.dr;
var newCol = self.boardCol + orthDir.dc;
if (!isValidSquare(newRow, newCol)) {
continue;
}
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'minister':
// Minister: 1 square diagonally, when promoted moves like rook and knight with jumping
if (self.isPromoted) {
// Rook-like moves (can jump over pieces when promoted)
var rookDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < rookDirections.length; i++) {
var dir = rookDirections[i];
for (var dist = 1; dist < 11; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
// Knight-like moves (can jump over pieces)
var knightMoves = [{
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}, {
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
}, {
dr: 1,
dc: 2
}, {
dr: 1,
dc: -2
}, {
dr: -1,
dc: 2
}, {
dr: -1,
dc: -2
}];
for (var i = 0; i < knightMoves.length; i++) {
var move = knightMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
} else {
// Standard minister: 1 square diagonally
directions = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
break;
case 'royalGuard':
// Up to 3 squares diagonally with blockage of friendly pieces
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
for (var dist = 1; dist <= 3; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
// Only 2 squares along rows and columns (no 1-step moves)
var orthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < orthogonalDirections.length; i++) {
var dir = orthogonalDirections[i];
// Only allow 2-step moves in orthogonal directions
var dist = 2;
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// Forward or backward L-shaped moves (no left/right L moves)
var lShapeMoves = [
// Forward L-shapes
{
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
},
// Backward L-shapes
{
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}];
for (var i = 0; i < lShapeMoves.length; i++) {
var move = lShapeMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'giraffe':
// Giraffe: one diagonal move followed by 3-10 straight moves with obstacle checking
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var diagDir = diagonalDirections[i];
var firstRow = self.boardRow + diagDir.dr;
var firstCol = self.boardCol + diagDir.dc;
if (!isValidSquare(firstRow, firstCol)) {
continue;
}
var firstPiece = getPieceAt(firstRow, firstCol);
if (firstPiece) {
continue; // First diagonal square must be empty
}
// Determine straight directions based on diagonal direction
var straightDirections = [];
if (diagDir.dr > 0) {
straightDirections.push({
dr: 1,
dc: 0
}); // downward
}
if (diagDir.dr < 0) {
straightDirections.push({
dr: -1,
dc: 0
}); // upward
}
if (diagDir.dc > 0) {
straightDirections.push({
dr: 0,
dc: 1
}); // rightward
}
if (diagDir.dc < 0) {
straightDirections.push({
dr: 0,
dc: -1
}); // leftward
}
// Try straight moves from 3 to 10 squares in each valid direction
for (var j = 0; j < straightDirections.length; j++) {
var straightDir = straightDirections[j];
for (var step = 3; step <= 10; step++) {
var newRow = firstRow + straightDir.dr * step;
var newCol = firstCol + straightDir.dc * step;
if (!isValidSquare(newRow, newCol)) {
break;
}
// Check if path is clear from diagonal position to target
var pathClear = true;
for (var checkStep = 1; checkStep <= step; checkStep++) {
var checkRow = firstRow + straightDir.dr * checkStep;
var checkCol = firstCol + straightDir.dc * checkStep;
var checkPiece = getPieceAt(checkRow, checkCol);
if (checkPiece) {
if (checkStep === step && checkPiece.pieceColor !== self.pieceColor) {
// Can capture enemy piece at final position
moves.push({
row: newRow,
col: newCol
});
}
pathClear = false;
break;
}
}
if (pathClear) {
moves.push({
row: newRow,
col: newCol
});
}
if (!pathClear) {
break; // Stop checking longer distances in this direction
}
}
}
}
break;
case 'bull':
// Bull can move one column forward or backward
var columnDirections = [{
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < columnDirections.length; i++) {
var colDir = columnDirections[i];
var newRow = self.boardRow + colDir.dr;
var newCol = self.boardCol + colDir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// Bull moves one square forward or backward in column, then unlimited diagonal movement in same direction
for (var i = 0; i < columnDirections.length; i++) {
var colDir = columnDirections[i];
var firstRow = self.boardRow + colDir.dr;
var firstCol = self.boardCol + colDir.dc;
if (!isValidSquare(firstRow, firstCol)) {
continue;
}
var firstPiece = getPieceAt(firstRow, firstCol);
if (firstPiece) {
continue; // First square must be empty for bull movement
}
// From the first square, move diagonally unlimited in the forward/backward direction
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var j = 0; j < diagonalDirections.length; j++) {
var diagDir = diagonalDirections[j];
// Only allow diagonal movement in same forward/backward direction as column move
if (colDir.dr > 0 && diagDir.dr <= 0 || colDir.dr < 0 && diagDir.dr >= 0) {
continue; // Skip diagonal directions that don't match column direction
}
// Move unlimited squares diagonally from first position
for (var dist = 1; dist < 11; dist++) {
var finalRow = firstRow + diagDir.dr * dist;
var finalCol = firstCol + diagDir.dc * dist;
if (!isValidSquare(finalRow, finalCol)) {
break;
}
var finalPiece = getPieceAt(finalRow, finalCol);
if (finalPiece) {
if (finalPiece.pieceColor !== self.pieceColor) {
moves.push({
row: finalRow,
col: finalCol
});
}
break;
}
moves.push({
row: finalRow,
col: finalCol
});
}
}
}
// Bull can also jump two squares left and right
var leftRightJumps = [{
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}];
for (var i = 0; i < leftRightJumps.length; i++) {
var jump = leftRightJumps[i];
var newRow = self.boardRow + jump.dr;
var newCol = self.boardCol + jump.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'camel':
// 1 square column and row (orthogonal moves) - not allowed in traditional version
if (!isTraditionalVersion) {
var orthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < orthogonalDirections.length; i++) {
var dir = orthogonalDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
// 1 square long L (knight-like moves)
var knightMoves = [{
dr: 3,
dc: 1
}, {
dr: 3,
dc: -1
}, {
dr: -3,
dc: 1
}, {
dr: -3,
dc: -1
}, {
dr: 1,
dc: 3
}, {
dr: 1,
dc: -3
}, {
dr: -1,
dc: 3
}, {
dr: -1,
dc: -3
}];
for (var i = 0; i < knightMoves.length; i++) {
var move = knightMoves[i];
var newRow = self.boardRow + move.dr;
var newCol = self.boardCol + move.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
break;
case 'lion':
// Lion has three distinct movement patterns:
// 1. Move 3 squares diagonally in one direction
// 2. L-shaped moves (forward/backward 2 + right/left 1)
// 3. Move 2 diagonally + 1 forward/backward
// 1st Move: 3 squares diagonally (similar to original code)
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var pathBlocked = false;
for (var dist = 1; dist <= 3; dist++) {
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
break;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
break;
}
moves.push({
row: newRow,
col: newCol
});
}
}
// 2nd Move: L-shaped moves (forward/backward 2 + right/left 1)
var lShapeMoves = [{
dr: -2,
dc: 1
}, {
dr: -2,
dc: -1
}, {
dr: 2,
dc: 1
}, {
dr: 2,
dc: -1
}];
for (var i = 0; i < lShapeMoves.length; i++) {
var move = lShapeMoves[i];
var midRow = self.boardRow + move.dr / 2;
var endRow = self.boardRow + move.dr;
var endCol = self.boardCol + move.dc;
// Check if vertical path is clear (first two squares)
if (!isValidSquare(midRow, self.boardCol) || getPieceAt(midRow, self.boardCol)) {
continue;
}
// Check for diagonal obstacles
var diagonalBlocked = false;
if (move.dr < 0) {
// moving up
if (move.dc == -1 && getPieceAt(self.boardRow - 1, self.boardCol - 1)) {
diagonalBlocked = true;
}
if (move.dc == 1 && getPieceAt(self.boardRow - 1, self.boardCol + 1)) {
diagonalBlocked = true;
}
} else {
// moving down
if (move.dc == -1 && getPieceAt(self.boardRow + 1, self.boardCol - 1)) {
diagonalBlocked = true;
}
if (move.dc == 1 && getPieceAt(self.boardRow + 1, self.boardCol + 1)) {
diagonalBlocked = true;
}
}
if (diagonalBlocked) {
continue;
}
if (isValidSquare(endRow, endCol)) {
var piece = getPieceAt(endRow, endCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: endRow,
col: endCol
});
}
}
}
// 3rd Move: 2 diagonals + 1 forward/backward
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var pathBlocked = false;
// Check if we can move 2 squares diagonally
for (var checkDist = 1; checkDist <= 2; checkDist++) {
var checkRow = self.boardRow + dir.dr * checkDist;
var checkCol = self.boardCol + dir.dc * checkDist;
if (!isValidSquare(checkRow, checkCol)) {
pathBlocked = true;
break;
}
var pathPiece = getPieceAt(checkRow, checkCol);
if (pathPiece) {
pathBlocked = true;
break;
}
}
if (pathBlocked) {
continue;
}
var twoSquareRow = self.boardRow + dir.dr * 2;
var twoSquareCol = self.boardCol + dir.dc * 2;
// Then move 1 square forward or backward
var verticalMoves = [{
dr: -1,
dc: 0
}, {
dr: 1,
dc: 0
}];
for (var j = 0; j < verticalMoves.length; j++) {
var vertMove = verticalMoves[j];
var finalRow = twoSquareRow + vertMove.dr;
var finalCol = twoSquareCol + vertMove.dc;
if (isValidSquare(finalRow, finalCol)) {
var piece = getPieceAt(finalRow, finalCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: finalRow,
col: finalCol
});
}
}
}
}
// Remove side L-shaped moves from the moves list
moves = moves.filter(function (move) {
return !isSideLMove(self.boardRow, self.boardCol, move.row, move.col);
});
break;
case 'pawnOfTheWarMachine':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawnOfTheWarMachine' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'camelPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'camelPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'ministersPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'ministersPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnOfTheElephant':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawnOfTheElephant' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'shahsade':
// King-like movement: 1 square in any direction
var kingDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// 2 squares diagonally, horizontally, vertically - not allowed in traditional version
if (!isTraditionalVersion) {
var shahsadeDirections = [{
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}, {
dr: 2,
dc: 0
}, {
dr: -2,
dc: 0
}, {
dr: 2,
dc: 2
}, {
dr: 2,
dc: -2
}, {
dr: -2,
dc: 2
}, {
dr: -2,
dc: -2
}];
for (var i = 0; i < shahsadeDirections.length; i++) {
var dir = shahsadeDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
// Check if path is blocked by any piece
var pathBlocked = false;
var stepRow = dir.dr === 0 ? 0 : dir.dr > 0 ? 1 : -1;
var stepCol = dir.dc === 0 ? 0 : dir.dc > 0 ? 1 : -1;
var checkRow = self.boardRow + stepRow;
var checkCol = self.boardCol + stepCol;
var pathPiece = getPieceAt(checkRow, checkCol);
if (pathPiece) {
pathBlocked = true;
}
if (!pathBlocked) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
}
break;
case 'adventurousKing':
// King-like movement: 1 square in any direction
var kingDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var i = 0; i < kingDirections.length; i++) {
var dir = kingDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
// 2 squares diagonally, horizontally, vertically (adventurous Shah can jump over pieces) - not allowed in traditional version
if (!isTraditionalVersion) {
var adventurousKingDirections = [{
dr: 0,
dc: 2
}, {
dr: 0,
dc: -2
}, {
dr: 2,
dc: 0
}, {
dr: -2,
dc: 0
}, {
dr: 2,
dc: 2
}, {
dr: 2,
dc: -2
}, {
dr: -2,
dc: 2
}, {
dr: -2,
dc: -2
}];
for (var i = 0; i < adventurousKingDirections.length; i++) {
var dir = adventurousKingDirections[i];
var newRow = self.boardRow + dir.dr;
var newCol = self.boardCol + dir.dc;
if (isValidSquare(newRow, newCol)) {
// Adventurous Shah can jump over pieces - no path blocking check
var piece = getPieceAt(newRow, newCol);
if (!piece || piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
}
}
break;
case 'kingsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 7 : 2;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'kingsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'viziersPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'viziersPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'giraffePawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'giraffePawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'knightPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'knightPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'rookPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'rookPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnOfTheLeader':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start (not allowed in traditional version)
if (!isTraditionalVersion && self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant (not allowed in traditional version)
if (!isTraditionalVersion && lastMove && lastMove.piece.pieceType === 'pawnOfTheLeader' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'guardsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
// Forward move (no double move for Guard's Pawn)
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'guardsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnOfTheBull':
var direction = self.pieceColor === 0 ? -1 : 1;
// Forward move (no double move for Pawn of the Bull)
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'pawnOfTheBull' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'lionsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
// Forward move (no double move for Lion's Pawn)
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'lionsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'pawnsPawn':
var direction = self.pieceColor === 0 ? -1 : 1;
var startRow = self.pieceColor === 0 ? 8 : 3;
// Forward move
if (isValidSquare(self.boardRow + direction, self.boardCol) && !getPieceAt(self.boardRow + direction, self.boardCol)) {
moves.push({
row: self.boardRow + direction,
col: self.boardCol
});
// Double move from start
if (self.boardRow === startRow && !getPieceAt(self.boardRow + 2 * direction, self.boardCol)) {
moves.push({
row: self.boardRow + 2 * direction,
col: self.boardCol
});
}
}
// Diagonal captures
for (var dc = -1; dc <= 1; dc += 2) {
var newRow = self.boardRow + direction;
var newCol = self.boardCol + dc;
if (isValidSquare(newRow, newCol)) {
var piece = getPieceAt(newRow, newCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: newRow,
col: newCol
});
}
}
// En passant
if (lastMove && lastMove.piece.pieceType === 'pawnsPawn' && Math.abs(lastMove.fromRow - lastMove.toRow) === 2) {
if (lastMove.toRow === self.boardRow && Math.abs(lastMove.toCol - self.boardCol) === 1) {
moves.push({
row: self.boardRow + direction,
col: lastMove.toCol,
isEnPassant: true,
captureRow: lastMove.toRow,
captureCol: lastMove.toCol
});
}
}
}
break;
case 'seaMonster':
var isInLeftRegion = isInRegion(self.boardRow, self.boardCol, 'left');
// 1. ÖZEL YEME ALANI (Görseldeki Kırmızı Bölgeler)
if (isInLeftRegion) {
// Sağa doğru 1, 2 ve 3. kolonları ve onların 1 üst/alt karelerini hedefle
// Bu liste görseldeki kırmızı karelerin tam koordinat karşılığıdır
var specialTargets = [
// 1 Kare Sağa ve Çaprazları
{
dr: 0,
dc: 1
}, {
dr: -1,
dc: 1
}, {
dr: 1,
dc: 1
},
// 2 Kare Sağa ve Çaprazları (Atlayarak)
{
dr: 0,
dc: 2
}, {
dr: -1,
dc: 2
}, {
dr: 1,
dc: 2
},
// 3 Kare Sağa ve Çaprazları (Atlayarak)
{
dr: 0,
dc: 3
}, {
dr: -1,
dc: 3
}, {
dr: 1,
dc: 3
}];
for (var j = 0; j < specialTargets.length; j++) {
var target = specialTargets[j];
var tRow = self.boardRow + target.dr;
var tCol = self.boardCol + target.dc;
if (isValidSquare(tRow, tCol)) {
var targetPiece = getPieceAt(tRow, tCol);
// ÖNEMLİ: Burada 'break' yok! Arada taş olsa bile hedefteki rakibi görür.
if (targetPiece && targetPiece.pieceColor !== self.pieceColor) {
moves.push({
row: tRow,
col: tCol,
isRemoteCapture: true // Yerinden hareket etmeden yer
});
}
}
}
}
// 2. NORMAL DENİZDE HAREKET (Yüzme)
// Sadece boş deniz karelerine gidebilir, taş yiyemez (yeme yukarıda tanımlandı)
var swimDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < swimDirections.length; i++) {
var d = swimDirections[i];
for (var dist = 1; dist <= 3; dist++) {
var sRow = self.boardRow + d.dr * dist;
var sCol = self.boardCol + d.dc * dist;
if (!isValidSquare(sRow, sCol)) break;
var pieceAtSquare = getPieceAt(sRow, sCol);
if (!pieceAtSquare && isSeaSquare(sRow, sCol)) {
moves.push({
row: sRow,
col: sCol
});
} else {
break; // Taş veya kara yolu kapatır
}
}
}
// 3. GENEL YAKIN MESAFE YEME (Dibindeki taşlar için güvenlik)
var neighbors = [{
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}, {
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
for (var k = 0; k < neighbors.length; k++) {
var nRow = self.boardRow + neighbors[k].dr;
var nCol = self.boardCol + neighbors[k].dc;
if (isValidSquare(nRow, nCol)) {
var nPiece = getPieceAt(nRow, nCol);
if (nPiece && nPiece.pieceColor !== self.pieceColor) {
// Eğer listede yoksa ekle
var alreadyAdded = moves.some(function (m) {
return m.row === nRow && m.col === nCol;
});
if (!alreadyAdded) {
moves.push({
row: nRow,
col: nCol,
isRemoteCapture: true
});
}
}
}
}
break;
}
// Filter out sea squares - pieces cannot enter the sea (except sea monster)
var filteredMoves = [];
for (var i = 0; i < moves.length; i++) {
if (!isSeaSquare(moves[i].row, moves[i].col) || self.pieceType === 'seaMonster') {
filteredMoves.push(moves[i]);
}
}
return filteredMoves;
};
self.getValidMoves = function () {
var moves = self.getRawMoves();
// Add castling for king after getting raw moves (not allowed in traditional version)
if (self.pieceType === 'king' && !self.hasMoved && !isInCheck(self.pieceColor) && !isTraditionalVersion) {
// Kingside castling - rook is at col + 5 (distance from king to rook)
var kingsideRook = getPieceAt(self.boardRow, self.boardCol + 5);
if (kingsideRook && kingsideRook.pieceType === 'rook' && !kingsideRook.hasMoved) {
var canCastle = true;
// Check squares between king and rook are empty and not attacked
for (var c = self.boardCol + 1; c < self.boardCol + 5; c++) {
if (getPieceAt(self.boardRow, c) || isSquareAttacked(self.boardRow, c, 1 - self.pieceColor)) {
canCastle = false;
break;
}
}
if (canCastle) {
moves.push({
row: self.boardRow,
col: self.boardCol + 2,
isCastling: true,
rookFromCol: self.boardCol + 5,
rookToCol: self.boardCol + 1
});
}
}
// Queenside castling - rook is at col - 5 (distance from king to rook)
var queensideRook = getPieceAt(self.boardRow, self.boardCol - 5);
if (queensideRook && queensideRook.pieceType === 'rook' && !queensideRook.hasMoved) {
var canCastle = true;
// Check squares between king and rook are empty and not attacked
for (var c = self.boardCol - 1; c > self.boardCol - 5; c--) {
if (getPieceAt(self.boardRow, c) || isSquareAttacked(self.boardRow, c, 1 - self.pieceColor)) {
canCastle = false;
break;
}
}
if (canCastle) {
moves.push({
row: self.boardRow,
col: self.boardCol - 2,
isCastling: true,
rookFromCol: self.boardCol - 5,
rookToCol: self.boardCol - 1
});
}
}
}
// Filter moves that would leave the king in check
var validMoves = [];
for (var i = 0; i < moves.length; i++) {
if (isValidMoveConsideringCheck(self, moves[i].row, moves[i].col)) {
// Additional check for sea squares - pieces cannot enter the sea (except sea monster)
if (isSeaSquare(moves[i].row, moves[i].col) && self.pieceType !== 'seaMonster') {
continue; // Skip sea squares
}
// Additional check for palace entry
if (isPalaceSquare(moves[i].row, moves[i].col)) {
if (canEnterPalace(self, moves[i].row, moves[i].col)) {
validMoves.push(moves[i]);
}
} else {
validMoves.push(moves[i]);
}
}
}
return validMoves;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
var BOARD_WIDTH = 15;
var BOARD_HEIGHT = 12;
var SQUARE_SIZE = 140;
var BOARD_START_X = (2048 - BOARD_WIDTH * SQUARE_SIZE) / 2;
var BOARD_START_Y = (2732 - BOARD_HEIGHT * SQUARE_SIZE) / 2 + 100;
var boardSquares = [];
var pieces = [];
var selectedPiece = null;
var highlightedSquares = [];
var currentPlayer = 0; // 0 = white, 1 = black
var gameStarted = false;
var menuVisible = true;
var menuContainer = null;
var lastMove = null;
var shahsadePromoted = [false, false]; // Track if shahsade has been promoted for each player
var pawnsPawnHasUsedSpecialMove = []; // Track which pawn's pawns have used their special move
var pawnsPawnMovementCount = []; // Track how many squares each pawn's pawn has moved
var shahPositionSwapUsed = [false, false]; // Track if Shah position swap has been used for each player
var swapHighlights = []; // Track swap highlight squares
function getBoardPosition(row, col) {
return {
x: BOARD_START_X + (col + 1) * SQUARE_SIZE + SQUARE_SIZE / 2,
y: BOARD_START_Y + row * SQUARE_SIZE + SQUARE_SIZE / 2
};
}
// Region definition system
var regions = {
left: {
name: 'Left Region',
rows: [0, 1],
// Rows A and B (0-indexed)
description: 'The left region of the board consisting of rows A and B'
},
right: {
name: 'Right Region',
rows: [12, 13],
// Rows M and N (0-indexed)
description: 'The right region of the board consisting of rows M and N'
},
upper: {
name: 'Upper Region',
cols: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
// Columns c, d, e, f, g, h, i, j, k, l (0-indexed as 2-11)
description: 'The upper region of the board consisting of columns c1, d1, e1, f1, g1, h1, i1, j1, k1, and l1'
},
lower: {
name: 'Lower Region',
cols: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
// Columns c, d, e, f, g, h, i, j, k, l (0-indexed as 2-11)
description: 'The lower region of the board consisting of columns c10, d10, e10, f10, g10, h10, i10, j10, k10, and l10'
}
};
function isInRegion(row, col, regionName) {
var region = regions[regionName];
if (!region) {
return false;
}
// Check if the row is within the region's defined rows
if (region.rows) {
return region.rows.indexOf(row) !== -1;
}
// Check if the column is within the region's defined columns and row is 1 (c1, d1, etc.) for upper region
if (region.cols && row === 1 && regionName === 'upper') {
return region.cols.indexOf(col) !== -1;
}
// Check if the column is within the region's defined columns and row is 10 (c10, d10, etc.) for lower region
if (region.cols && row === 10 && regionName === 'lower') {
return region.cols.indexOf(col) !== -1;
}
return false;
}
function getRegionName(row, col) {
for (var regionName in regions) {
if (isInRegion(row, col, regionName)) {
return regionName;
}
}
return null; // Not in any defined region
}
function getPiecesInRegion(regionName) {
var piecesInRegion = [];
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (isInRegion(piece.boardRow, piece.boardCol, regionName)) {
piecesInRegion.push(piece);
}
}
return piecesInRegion;
}
function addRegion(name, rows, description) {
regions[name] = {
name: name,
rows: rows,
description: description
};
}
function removeRegion(name) {
delete regions[name];
}
var aiMoveTimer = null;
var aiThinkingTime = 1500; // AI thinks for 1.5 seconds
function makeAIMove() {
// Prevent multiple AI moves
if (aiMoveTimer) {
return;
}
// Add thinking delay for better UX
aiMoveTimer = LK.setTimeout(function () {
aiMoveTimer = null;
var bestMove = findBestAIMove(aiColor);
if (bestMove) {
makeMove(bestMove.piece, bestMove.targetRow, bestMove.targetCol, bestMove.moveData);
}
}, aiThinkingTime);
}
function findBestAIMove(color) {
var allMoves = [];
// Collect all possible moves for AI
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor === color) {
var moves = piece.getValidMoves();
for (var j = 0; j < moves.length; j++) {
allMoves.push({
piece: piece,
targetRow: moves[j].row,
targetCol: moves[j].col,
moveData: moves[j],
score: evaluateMove(piece, moves[j])
});
}
}
}
if (allMoves.length === 0) {
return null;
}
// Sort moves by score (highest first)
allMoves.sort(function (a, b) {
return b.score - a.score;
});
// Return best move
return allMoves[0];
}
function evaluateMove(piece, move) {
var score = 0;
var targetPiece = getPieceAt(move.row, move.col);
// Prioritize captures
if (targetPiece) {
score += getPieceValue(targetPiece.pieceType) * 10;
// Extra points for capturing king
if (targetPiece.pieceType === 'king') {
score += 1000;
}
}
// Prefer center control
var centerRow = BOARD_HEIGHT / 2;
var centerCol = BOARD_WIDTH / 2;
var distanceFromCenter = Math.abs(move.row - centerRow) + Math.abs(move.col - centerCol);
score += 20 - distanceFromCenter;
// Avoid moving into danger
if (isSquareAttacked(move.row, move.col, 1 - piece.pieceColor)) {
score -= getPieceValue(piece.pieceType) * 5;
}
// Random factor to prevent predictable play
score += Math.random() * 10;
return score;
}
function getPieceValue(pieceType) {
var values = {
'pawn': 1,
'pawnOfTheWarMachine': 1,
'camelPawn': 1,
'ministersPawn': 1,
'pawnOfTheElephant': 1,
'kingsPawn': 1,
'viziersPawn': 1,
'giraffePawn': 1,
'knightPawn': 1,
'rookPawn': 1,
'pawnOfTheLeader': 1,
'guardsPawn': 1,
'pawnOfTheBull': 1,
'lionsPawn': 1,
'pawnsPawn': 1,
'knight': 3,
'bishop': 3,
'minister': 3,
'rook': 5,
'royalGuard': 5,
'giraffe': 5,
'bull': 5,
'camel': 5,
'warElephant': 5,
'siegeTower': 5,
'lion': 5,
'queen': 9,
'seaMonster': 9,
'king': 1000,
'shahsade': 500,
'adventurousKing': 500
};
return values[pieceType] || 1;
}
function getBoardCoordinates(x, y) {
var col = Math.floor((x - BOARD_START_X) / SQUARE_SIZE) - 1;
var row = Math.floor((y - BOARD_START_Y) / SQUARE_SIZE);
return {
row: row,
col: col
};
}
function positionToAlgebraic(row, col) {
var files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'];
var ranks = ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1'];
return files[col] + ranks[row];
}
function pieceToNotation(pieceType) {
var notationMap = {
'king': 'K',
'adventurousKing': 'AK',
'queen': 'V',
'rook': 'R',
'bishop': 'F',
'knight': 'N',
'warElephant': 'E',
'siegeTower': 'W',
'minister': 'F',
'royalGuard': 'RG',
'giraffe': 'G',
'bull': 'T',
'camel': 'C',
'lion': 'L',
'shahsade': 'K',
'pawn': 'PPP',
'pawnOfTheWarMachine': 'WP',
'camelPawn': 'CP',
'ministersPawn': 'FP',
'pawnOfTheElephant': 'EP',
'kingsPawn': 'KP',
'viziersPawn': 'VP',
'giraffePawn': 'GP',
'knightPawn': 'NP',
'rookPawn': 'RP',
'pawnOfTheLeader': 'BP',
'guardsPawn': 'RGP',
'pawnOfTheBull': 'TP',
'lionsPawn': 'LP',
'pawnsPawn': 'PP',
'seaMonster': 'SM'
};
return notationMap[pieceType] || '';
}
function isValidSquare(row, col) {
return row >= 0 && row < BOARD_HEIGHT && col >= -1 && col < BOARD_WIDTH;
}
function isSeaSquare(row, col) {
// Column -1 (column -1) squares are all sea (new column to the left of black palace)
// Column A (column 0) squares are sea except for the palace at row 2
// Column M (column 12) squares are all sea
// Column N (column 13) squares are sea except for the palace at row 9
// Column O (column 14) squares are all sea
// Row 0 is sea for all columns
// Row 11 is sea for all columns
return col === -1 || col === 0 && row !== 2 || col === 12 && row !== 9 || col === 13 || col === 14 || row === 0 || row === 11;
}
function getPieceAt(row, col) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].boardRow === row && pieces[i].boardCol === col) {
return pieces[i];
}
}
return null;
}
function createBoard() {
for (var row = 0; row < BOARD_HEIGHT; row++) {
boardSquares[row] = [];
for (var col = -1; col < BOARD_WIDTH; col++) {
// Sea rows: row 0 (above black pieces) and row 11 (below white pieces)
if (row === 0 || row === 11) {
// Create sea squares for top and bottom rows
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Column -1 squares - create sea squares for all rows in column -1
if (col === -1) {
// All squares in column -1 are sea
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Column A squares - create squares for all valid rows in column 0
if (col === 0) {
// Only row 2 has the black palace, other rows get regular squares
if (row !== 2) {
// Create sea squares for other rows in column A with separate textures
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
}
// Column M squares - create squares for all valid rows in column 12
if (col === 12) {
// Only row 9 has the white palace, other rows get sea squares
if (row !== 9) {
// Create sea squares for other rows in column M with separate textures
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
}
// Column N squares - create sea squares for all rows in column 13
if (col === 13) {
// All squares in column N are sea
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Column O squares - create sea squares for all rows in column 14
if (col === 14) {
// All squares in column O are sea
var isLight = (row + col) % 2 === 0;
var squareAsset = isLight ? 'seaTextureLight' : 'seaTextureDark';
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
square.isSea = true; // Mark as sea square
boardSquares[row][col] = square;
continue;
}
// Create squares for all rows in column 12
// Special case: Black Palace at column 0, row 2 (left of a10)
// Special case: White Palace at column 13, row 9 (right of l11)
var squareAsset;
if (col === 0 && row === 2) {
squareAsset = 'blackPalace';
} else if (col === 12 && row === 9) {
squareAsset = 'whitePalace';
} else {
var isLight = (row + col) % 2 === 0;
squareAsset = isLight ? 'lightSquare' : 'darkSquare';
}
var square = game.addChild(LK.getAsset(squareAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
var pos = getBoardPosition(row, col);
square.x = pos.x;
square.y = pos.y;
square.boardRow = row;
square.boardCol = col;
boardSquares[row][col] = square;
}
}
}
function createPieces() {
// Create pieces for both players
var pieceSetup = [
// White pieces (bottom)
{
type: 'rook',
color: 0,
row: 9,
col: 1
}, {
type: 'knight',
color: 0,
row: 9,
col: 2
}, {
type: 'warElephant',
color: 0,
row: 10,
col: 1
}, {
type: 'warElephant',
color: 0,
row: 10,
col: 11
}, {
type: 'bishop',
color: 0,
row: 9,
col: 3
}, {
type: 'minister',
color: 0,
row: 9,
col: 5
}, {
type: 'king',
color: 0,
row: 9,
col: 6
}, {
type: 'bishop',
color: 0,
row: 9,
col: 9
}, {
type: 'siegeTower',
color: 0,
row: 10,
col: 7
}, {
type: 'siegeTower',
color: 0,
row: 10,
col: 5
}, {
type: 'queen',
color: 0,
row: 9,
col: 7
}, {
type: 'giraffe',
color: 0,
row: 9,
col: 4
}, {
type: 'giraffe',
color: 0,
row: 9,
col: 8
}, {
type: 'knight',
color: 0,
row: 9,
col: 10
}, {
type: 'rook',
color: 0,
row: 9,
col: 11
},
// White royal guards
{
type: 'royalGuard',
color: 0,
row: 10,
col: 6
},
// White sea monster (behind royal guard)
{
type: 'seaMonster',
color: 0,
row: 11,
col: 6
},
// White lions
{
type: 'lion',
color: 0,
row: 10,
col: 2
}, {
type: 'lion',
color: 0,
row: 10,
col: 10
},
// White camels
{
type: 'camel',
color: 0,
row: 10,
col: 3
}, {
type: 'camel',
color: 0,
row: 10,
col: 9
},
// White bulls
{
type: 'bull',
color: 0,
row: 10,
col: 4
}, {
type: 'bull',
color: 0,
row: 10,
col: 8
},
// Black pieces (top) - all moved down by 1 row
{
type: 'rook',
color: 1,
row: 2,
col: 1
}, {
type: 'knight',
color: 1,
row: 2,
col: 2
}, {
type: 'warElephant',
color: 1,
row: 1,
col: 1
}, {
type: 'warElephant',
color: 1,
row: 1,
col: 11
}, {
type: 'bishop',
color: 1,
row: 2,
col: 3
}, {
type: 'minister',
color: 1,
row: 2,
col: 7
}, {
type: 'king',
color: 1,
row: 2,
col: 6
}, {
type: 'bishop',
color: 1,
row: 2,
col: 9
}, {
type: 'siegeTower',
color: 1,
row: 1,
col: 7
}, {
type: 'siegeTower',
color: 1,
row: 1,
col: 5
}, {
type: 'queen',
color: 1,
row: 2,
col: 5
}, {
type: 'giraffe',
color: 1,
row: 2,
col: 4
}, {
type: 'giraffe',
color: 1,
row: 2,
col: 8
}, {
type: 'knight',
color: 1,
row: 2,
col: 10
}, {
type: 'rook',
color: 1,
row: 2,
col: 11
},
// Black royal guards
{
type: 'royalGuard',
color: 1,
row: 1,
col: 6
},
// Black sea monster (behind royal guard)
{
type: 'seaMonster',
color: 1,
row: 0,
col: 6
},
// Black lions
{
type: 'lion',
color: 1,
row: 1,
col: 2
}, {
type: 'lion',
color: 1,
row: 1,
col: 10
},
// Black camels
{
type: 'camel',
color: 1,
row: 1,
col: 3
}, {
type: 'camel',
color: 1,
row: 1,
col: 9
},
// Black bulls
{
type: 'bull',
color: 1,
row: 1,
col: 4
}, {
type: 'bull',
color: 1,
row: 1,
col: 8
}];
// Add Pawn of the War Machine for each side
pieceSetup.push({
type: 'pawnOfTheWarMachine',
color: 0,
row: 8,
col: 2
}); // White Pawn of the War Machine
pieceSetup.push({
type: 'pawnOfTheWarMachine',
color: 1,
row: 3,
col: 10
}); // Black Pawn of the War Machine
// Add Camel Pawn for each side
pieceSetup.push({
type: 'camelPawn',
color: 0,
row: 8,
col: 3
}); // White Camel Pawn
pieceSetup.push({
type: 'camelPawn',
color: 1,
row: 3,
col: 9
}); // Black Camel Pawn
// Add Pawn of the Elephant for each side
pieceSetup.push({
type: 'pawnOfTheElephant',
color: 0,
row: 8,
col: 4
}); // White Pawn of the Elephant
pieceSetup.push({
type: 'pawnOfTheElephant',
color: 1,
row: 3,
col: 8
}); // Black Pawn of the Elephant
// Add Minister's Pawn for each side
pieceSetup.push({
type: 'ministersPawn',
color: 0,
row: 8,
col: 5
}); // White Minister's Pawn
pieceSetup.push({
type: 'ministersPawn',
color: 1,
row: 3,
col: 7
}); // Black Minister's Pawn
// Add King's Pawn for each side
pieceSetup.push({
type: 'kingsPawn',
color: 0,
row: 8,
col: 6
}); // White King's Pawn
pieceSetup.push({
type: 'kingsPawn',
color: 1,
row: 3,
col: 6
}); // Black King's Pawn
// Add Vizier's Pawn for each side
pieceSetup.push({
type: 'viziersPawn',
color: 0,
row: 8,
col: 7
}); // White Vizier's Pawn
pieceSetup.push({
type: 'viziersPawn',
color: 1,
row: 3,
col: 5
}); // Black Vizier's Pawn
// Add Giraffe Pawn for each side
pieceSetup.push({
type: 'giraffePawn',
color: 0,
row: 8,
col: 8
}); // White Giraffe Pawn
pieceSetup.push({
type: 'giraffePawn',
color: 1,
row: 3,
col: 4
}); // Black Giraffe Pawn
// Add Knight Pawn for each side
pieceSetup.push({
type: 'knightPawn',
color: 0,
row: 8,
col: 10
}); // White Knight Pawn
pieceSetup.push({
type: 'knightPawn',
color: 1,
row: 3,
col: 2
}); // Black Knight Pawn
// Add Rook Pawn for each side
pieceSetup.push({
type: 'rookPawn',
color: 0,
row: 8,
col: 11
}); // White Rook Pawn
pieceSetup.push({
type: 'rookPawn',
color: 1,
row: 3,
col: 1
}); // Black Rook Pawn
// Add Pawn of the Leader for each side
pieceSetup.push({
type: 'pawnOfTheLeader',
color: 0,
row: 8,
col: 9
}); // White Pawn of the Leader
pieceSetup.push({
type: 'pawnOfTheLeader',
color: 1,
row: 3,
col: 3
}); // Black Pawn of the Leader
// Add Guard's Pawn for each side - in front of the King's Pawn
pieceSetup.push({
type: 'guardsPawn',
color: 0,
row: 7,
col: 6
}); // White Guard's Pawn (in front of White King's Pawn)
pieceSetup.push({
type: 'guardsPawn',
color: 1,
row: 4,
col: 6
}); // Black Guard's Pawn (in front of Black King's Pawn)
// Add Pawn of the Bull for each side - in front of the Pawn of the Leader
pieceSetup.push({
type: 'pawnOfTheBull',
color: 0,
row: 7,
col: 9
}); // White Pawn of the Bull (in front of White Pawn of the Leader)
pieceSetup.push({
type: 'pawnOfTheBull',
color: 1,
row: 4,
col: 3
}); // Black Pawn of the Bull (in front of Black Pawn of the Leader)
// Add Lion's Pawn for each side - in front of the Camel's Pawn
pieceSetup.push({
type: 'lionsPawn',
color: 0,
row: 7,
col: 3
}); // White Lion's Pawn (in front of White Camel Pawn)
pieceSetup.push({
type: 'lionsPawn',
color: 1,
row: 4,
col: 9
}); // Black Lion's Pawn (in front of Black Camel Pawn)
// Add regular pawn for each side
pieceSetup.push({
type: 'pawn',
color: 0,
row: 8,
col: 1
}); // White Pawn
pieceSetup.push({
type: 'pawn',
color: 1,
row: 3,
col: 11
}); // Black Pawn
for (var i = 0; i < pieceSetup.length; i++) {
var setup = pieceSetup[i];
// Skip modern pieces in traditional version
if (isTraditionalVersion) {
var modernPieces = ['lion', 'bull', 'seaMonster', 'royalGuard', 'guardsPawn', 'pawnOfTheBull', 'lionsPawn'];
if (modernPieces.indexOf(setup.type) !== -1) {
continue; // Skip this piece
}
}
var piece = game.addChild(new ChessPiece(setup.type, setup.color, setup.row, setup.col));
var pos = getBoardPosition(setup.row, setup.col);
piece.x = pos.x;
piece.y = pos.y;
pieces.push(piece);
}
}
function clearHighlights() {
for (var i = 0; i < highlightedSquares.length; i++) {
highlightedSquares[i].destroy();
}
highlightedSquares = [];
}
function clearSwapHighlights() {
for (var i = 0; i < swapHighlights.length; i++) {
swapHighlights[i].destroy();
}
swapHighlights = [];
}
function highlightMoves(moves) {
clearHighlights();
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
var assetName = move.isCastling ? 'castlingSquare' : 'highlightSquare';
var highlight = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
}));
var pos = getBoardPosition(move.row, move.col);
highlight.x = pos.x;
highlight.y = pos.y;
highlightedSquares.push(highlight);
}
}
function highlightSwappablePieces(swappablePieces) {
clearSwapHighlights();
for (var i = 0; i < swappablePieces.length; i++) {
var piece = swappablePieces[i];
var highlight = game.addChild(LK.getAsset('swapSquare', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
var pos = getBoardPosition(piece.boardRow, piece.boardCol);
highlight.x = pos.x;
highlight.y = pos.y;
swapHighlights.push(highlight);
}
}
function selectPiece(piece) {
if (selectedPiece) {
selectedPiece.removeChild(selectedPiece.selectionHighlight);
selectedPiece.selectionHighlight = null;
}
selectedPiece = piece;
if (piece) {
piece.selectionHighlight = piece.addChild(LK.getAsset('selectedSquare', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
var validMoves = piece.getValidMoves();
highlightMoves(validMoves);
// If selecting a Shah (king), also highlight swappable pieces
if (piece.pieceType === 'king') {
var swappablePieces = getSwappablePieces(piece);
highlightSwappablePieces(swappablePieces);
}
} else {
clearHighlights();
clearSwapHighlights();
}
}
function generateMoveNotation(piece, targetRow, targetCol, moveData, capturedPieces) {
var notation = '';
var pieceNotation = pieceToNotation(piece.pieceType);
var targetSquare = positionToAlgebraic(targetRow, targetCol);
// Handle special moves
if (moveData && moveData.isCastling) {
notation = targetCol > piece.boardCol ? 'O-O' : 'O-O-O';
} else {
// Add piece notation
notation += pieceNotation;
// Add capture notation
if (capturedPieces.length > 0) {
if (pieceNotation === '') {
// For pawn captures, add the file of origin
notation += positionToAlgebraic(piece.boardRow, piece.boardCol).charAt(0);
}
notation += 'x';
}
// Add destination square
notation += targetSquare;
}
return notation;
}
function makeMove(piece, targetRow, targetCol, moveData) {
var originalRow = piece.boardRow;
var originalCol = piece.boardCol;
// Check palace entry restrictions
if (isPalaceSquare(targetRow, targetCol)) {
if (!canEnterPalace(piece, targetRow, targetCol)) {
return; // Invalid move, cannot enter palace
}
}
var capturedPiece = null;
var capturedPieces = [];
// Handle special moves
if (moveData && moveData.isCastling) {
// Move the rook for castling
var rook = getPieceAt(originalRow, moveData.rookFromCol);
if (rook) {
rook.moveTo(originalRow, moveData.rookToCol);
}
// Play castle sound
LK.getSound('castle').play();
} else if (moveData && moveData.isEnPassant) {
// Capture the pawn for en passant
capturedPiece = getPieceAt(moveData.captureRow, moveData.captureCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
}
} else if (moveData && moveData.capturesInPath) {
// Handle siegeTower path captures
for (var k = 0; k < moveData.capturesInPath.length; k++) {
var capturePos = moveData.capturesInPath[k];
var pathPiece = getPieceAt(capturePos.row, capturePos.col);
if (pathPiece) {
capturedPieces.push(pathPiece);
}
}
} else {
// Handle remote capture for sea monster
if (moveData && moveData.isRemoteCapture) {
capturedPiece = getPieceAt(targetRow, targetCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
}
// Don't actually move the sea monster for remote captures
piece.hasMoved = true; // Mark as moved for game logic
// Skip the actual position change by returning early after cleanup
} else {
capturedPiece = getPieceAt(targetRow, targetCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
// Apply saray capture flag if needed
if (isSaray(targetRow, targetCol)) {
handleSarayCapture(piece, targetRow, targetCol);
}
}
}
}
// Remove all captured pieces
for (var k = 0; k < capturedPieces.length; k++) {
var capPiece = capturedPieces[k];
var index = pieces.indexOf(capPiece);
if (index > -1) {
pieces.splice(index, 1);
}
capPiece.destroy();
}
if (capturedPieces.length > 0) {
LK.getSound('capture').play();
} else {
LK.getSound('move').play();
}
// Store move for en passant detection
lastMove = {
piece: piece,
fromRow: originalRow,
fromCol: originalCol,
toRow: targetRow,
toCol: targetCol
};
// Only move piece if it's not a remote capture
if (!moveData || !moveData.isRemoteCapture) {
piece.moveTo(targetRow, targetCol);
}
// Track movement for pawn's pawn
if (piece.pieceType === 'pawnsPawn') {
for (var mc = 0; mc < pawnsPawnMovementCount.length; mc++) {
if (pawnsPawnMovementCount[mc].piece === piece) {
pawnsPawnMovementCount[mc].moves++;
break;
}
}
}
// Check for vizier promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'queen' && !piece.isPromoted && !isTraditionalVersion) {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
piece.isPromoted = true;
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for minister promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'minister' && !piece.isPromoted && !isTraditionalVersion) {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
piece.isPromoted = true;
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x9932CC, 1000); // Flash purple
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the War Machine promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheWarMachine') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into siege tower
piece.pieceType = 'siegeTower';
// Change texture to siege tower
piece.removeChildAt(0); // Remove old texture
var siegeTowerGraphics = piece.attachAsset('siegeTower', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
siegeTowerGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x00FF00, 1000); // Flash green
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Camel Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'camelPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into camel
piece.pieceType = 'camel';
// Change texture to camel
piece.removeChildAt(0); // Remove old texture
var camelGraphics = piece.attachAsset('camel', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
camelGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFFF00, 1000); // Flash yellow
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Minister's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'ministersPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into minister (promoted only if not traditional version)
piece.pieceType = 'minister';
piece.isPromoted = !isTraditionalVersion;
// Change texture to minister
piece.removeChildAt(0); // Remove old texture
var ministerGraphics = piece.attachAsset('minister', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
ministerGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x9932CC, 1000); // Flash purple
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the Elephant promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheElephant') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into war elephant
piece.pieceType = 'warElephant';
// Change texture to war elephant
piece.removeChildAt(0); // Remove old texture
var warElephantGraphics = piece.attachAsset('warElephant', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
warElephantGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x8B4513, 1000); // Flash brown
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for King's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'kingsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into shahsade
piece.pieceType = 'shahsade';
// Change texture to shahsade
piece.removeChildAt(0); // Remove old texture
var shahsadeGraphics = piece.attachAsset('shahsade', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
shahsadeGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFF4500, 1000); // Flash orange red
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Vizier's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'viziersPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into vizier (promoted only if not traditional version)
piece.pieceType = 'queen';
piece.isPromoted = !isTraditionalVersion;
// Change texture to queen
piece.removeChildAt(0); // Remove old texture
var queenGraphics = piece.attachAsset('queen', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
queenGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Giraffe Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'giraffePawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into giraffe
piece.pieceType = 'giraffe';
// Change texture to giraffe
piece.removeChildAt(0); // Remove old texture
var giraffeGraphics = piece.attachAsset('giraffe', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
giraffeGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFA500, 1000); // Flash orange
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Knight Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'knightPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into knight
piece.pieceType = 'knight';
// Change texture to knight
piece.removeChildAt(0); // Remove old texture
var knightGraphics = piece.attachAsset('knight', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
knightGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x00CED1, 1000); // Flash dark turquoise
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Rook Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'rookPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into rook
piece.pieceType = 'rook';
// Change texture to rook
piece.removeChildAt(0); // Remove old texture
var rookGraphics = piece.attachAsset('rook', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
rookGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x0000FF, 1000); // Flash blue
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the Leader promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheLeader') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into bishop
piece.pieceType = 'bishop';
// Change texture to bishop
piece.removeChildAt(0); // Remove old texture
var bishopGraphics = piece.attachAsset('bishop', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
bishopGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x800080, 1000); // Flash purple
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Guard's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'guardsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into royalGuard
piece.pieceType = 'royalGuard';
// Change texture to royalGuard
piece.removeChildAt(0); // Remove old texture
var royalGuardGraphics = piece.attachAsset('royalGuard', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
royalGuardGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xCD5C5C, 1000); // Flash indian red
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn of the Bull promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnOfTheBull') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into bull
piece.pieceType = 'bull';
// Change texture to bull
piece.removeChildAt(0); // Remove old texture
var bullGraphics = piece.attachAsset('bull', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
bullGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xB8860B, 1000); // Flash dark goldenrod
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Lion's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'lionsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into lion
piece.pieceType = 'lion';
// Change texture to lion
piece.removeChildAt(0); // Remove old texture
var lionGraphics = piece.attachAsset('lion', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
lionGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for regular pawn promotion to pawn's pawn - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Transform into pawn's pawn
piece.pieceType = 'pawnsPawn';
// Initialize movement count for this pawn's pawn
pawnsPawnMovementCount.push({
piece: piece,
moves: 0
});
// Change texture to pawn's pawn
piece.removeChildAt(0); // Remove old texture
var pawnsPawnGraphics = piece.attachAsset('pawnsPawn', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
pawnsPawnGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFF69B4, 1000); // Flash hot pink
// Play promotion sound
LK.getSound('promotion').play();
}
}
// Check for Pawn's Pawn promotion - adjusted to one square before last row due to sea squares
if (piece.pieceType === 'pawnsPawn') {
var opponentPromotionRow = piece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (targetRow === opponentPromotionRow) {
// Find movement count for this piece
var moveCount = 0;
for (var mc = 0; mc < pawnsPawnMovementCount.length; mc++) {
if (pawnsPawnMovementCount[mc].piece === piece) {
moveCount = pawnsPawnMovementCount[mc].moves;
break;
}
}
// Must move at least 1 square to be promoted
if (moveCount >= 1) {
// Transform into adventurous king
piece.pieceType = 'adventurousKing';
// Change texture to adventurous king
piece.removeChildAt(0); // Remove old texture
var adventurousKingGraphics = piece.attachAsset('adventurousKing', {
anchorX: 0.5,
anchorY: 0.5
});
if (piece.pieceColor === 1) {
adventurousKingGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x00FFFF, 1000); // Flash cyan
// Play promotion sound
LK.getSound('promotion').play();
}
}
}
// Removed adventurous king promotion to king when entering own saray
// Check for shahsade victory condition - capturing king with shahsade is immediate win
if (piece.pieceType === 'shahsade' && capturedPieces.length > 0) {
for (var k = 0; k < capturedPieces.length; k++) {
if (capturedPieces[k].pieceType === 'king') {
// Shahsade captured the king - immediate victory
LK.setScore(1);
LK.showYouWin();
return;
}
}
}
// Generate and record move notation
var notation = generateMoveNotation(piece, targetRow, targetCol, moveData, capturedPieces);
var moveNumber = Math.floor(moveHistory.length / 2) + 1;
var colorPrefix = currentPlayer === 0 ? moveNumber + '.' : '';
moveHistory.push(colorPrefix + notation);
// Update move history display only if visible
if (moveHistoryVisible) {
var displayText = 'Move History:\n';
for (var i = 0; i < moveHistory.length; i++) {
if (i % 2 === 0) {
displayText += moveHistory[i] + ' ';
} else {
displayText += moveHistory[i] + '\n';
}
}
moveHistoryText.setText(displayText);
}
// Check for draw offer if piece entered opponent's palace
if (isPalaceSquare(targetRow, targetCol)) {
if (offerDraw(piece)) {
return; // Game ended in draw
}
}
// Check if king must exit saray on next turn after move
checkIfKingMustExitNextTurn(currentPlayer);
currentPlayer = 1 - currentPlayer;
selectPiece(null);
// Check for victory conditions
checkGameEnd();
}
function isSquareAttacked(row, col, byColor) {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor !== byColor) {
continue;
}
var moves = piece.getRawMoves();
for (var j = 0; j < moves.length; j++) {
if (moves[j].row === row && moves[j].col === col) {
return true;
}
}
}
return false;
}
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;
}
// Check if the defending player has shahsade or adventurous king
var defendingHasShahsade = false;
var defendingHasAdventurousKing = false;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color) {
if (pieces[i].pieceType === 'shahsade') {
defendingHasShahsade = true;
}
if (pieces[i].pieceType === 'adventurousKing') {
defendingHasAdventurousKing = true;
}
}
}
// If defending player has no shahsade or adventurous king, king can be checked normally
if (!defendingHasShahsade && !defendingHasAdventurousKing) {
return isSquareAttacked(king.boardRow, king.boardCol, 1 - color);
}
// Check if the attacking player has a shahsade
var attackingColor = 1 - color;
var hasShahsade = false;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === attackingColor && pieces[i].pieceType === 'shahsade') {
hasShahsade = true;
break;
}
}
// If attacker has shahsade and it hasn't been promoted, they cannot check the king
if (hasShahsade && !shahsadePromoted[attackingColor]) {
return false;
}
return isSquareAttacked(king.boardRow, king.boardCol, 1 - color);
}
function isValidMoveConsideringCheck(piece, targetRow, targetCol) {
// Simulate the move
var originalRow = piece.boardRow;
var originalCol = piece.boardCol;
var capturedPiece = getPieceAt(targetRow, targetCol);
var capturedIndex = -1;
if (capturedPiece) {
capturedIndex = pieces.indexOf(capturedPiece);
pieces.splice(capturedIndex, 1);
}
piece.boardRow = targetRow;
piece.boardCol = targetCol;
// Convert adventurous king if needed after simulated move
convertAdventurousKingIfNeeded(piece.pieceColor);
var stillInCheck = isInCheck(piece.pieceColor);
var hasImmunity = hasImmunityPiece(piece.pieceColor);
// Restore the position
piece.boardRow = originalRow;
piece.boardCol = originalCol;
if (capturedPiece && capturedIndex > -1) {
pieces.splice(capturedIndex, 0, capturedPiece);
}
// Player can ignore check only if they have immunity piece
if (hasImmunity) {
return true;
}
// Otherwise check must be resolved
return !stillInCheck;
}
function hasValidMoves(color) {
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
if (piece.pieceColor !== color) {
continue;
}
var moves = piece.getRawMoves();
for (var j = 0; j < moves.length; j++) {
if (isValidMoveConsideringCheck(piece, moves[j].row, moves[j].col)) {
return true;
}
}
}
return false;
}
function isSideLMove(fromRow, fromCol, toRow, toCol) {
var rowDiff = Math.abs(toRow - fromRow);
var colDiff = Math.abs(toCol - fromCol);
// Yan L: 2 yatay (col), 1 dikey (row)
if (rowDiff === 1 && colDiff === 2) {
return true; // Bu hareket yan L hareketidir
}
return false;
}
function hasRealKing(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color && pieces[i].pieceType === 'king') {
return true;
}
}
return false;
}
function hasAdventurousKing(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color && pieces[i].pieceType === 'adventurousKing') {
return true;
}
}
return false;
}
function hasShahsade(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color && pieces[i].pieceType === 'shahsade') {
return true;
}
}
return false;
}
function convertAdventurousKingIfNeeded(color) {
if (hasRealKing(color) || hasShahsade(color)) {
return;
}
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === color && p.pieceType === 'adventurousKing') {
p.pieceType = 'king';
// Change texture to king
p.removeChildAt(0); // Remove old texture
var kingGraphics = p.attachAsset('king', {
anchorX: 0.5,
anchorY: 0.5
});
if (p.pieceColor === 1) {
kingGraphics.tint = 0x333333;
}
// Visual feedback for conversion
LK.effects.flashObject(p, 0xFFD700, 1000); // Flash gold
console.log('AdventurousKing converted to KING at (' + r + ',' + c + ')');
return;
}
}
}
}
function checkGameEnd() {
// Convert adventurous kings if needed for both players
convertAdventurousKingIfNeeded(0);
convertAdventurousKingIfNeeded(1);
var whiteKing = findKing(0);
var blackKing = findKing(1);
// Check if a player who brought shahsade into play loses their king
if (!whiteKing) {
// White king is captured - check if white has shahsade
var whiteShahsade = null;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === 0 && pieces[i].pieceType === 'shahsade') {
whiteShahsade = pieces[i];
break;
}
}
if (whiteShahsade) {
// Promote shahsade to new shah (king)
whiteShahsade.pieceType = 'king';
// Mark that white's shahsade has been promoted
shahsadePromoted[0] = true;
// Change texture to king
whiteShahsade.removeChildAt(0); // Remove old texture
var kingGraphics = whiteShahsade.attachAsset('king', {
anchorX: 0.5,
anchorY: 0.5
});
if (whiteShahsade.pieceColor === 1) {
kingGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(whiteShahsade, 0xFFD700, 1000); // Flash gold
// Check if player is in checkmate after promotion
if (isInCheck(0) && !hasValidMoves(0)) {
LK.setScore(1);
LK.showGameOver();
return;
}
// Game continues, don't end
return;
} else {
LK.setScore(1);
LK.showGameOver();
return;
}
}
if (!blackKing) {
// Black king is captured - check if black has shahsade
var blackShahsade = null;
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === 1 && pieces[i].pieceType === 'shahsade') {
blackShahsade = pieces[i];
break;
}
}
if (blackShahsade) {
// Promote shahsade to new shah (king)
blackShahsade.pieceType = 'king';
// Mark that black's shahsade has been promoted
shahsadePromoted[1] = true;
// Change texture to king
blackShahsade.removeChildAt(0); // Remove old texture
var kingGraphics = blackShahsade.attachAsset('king', {
anchorX: 0.5,
anchorY: 0.5
});
if (blackShahsade.pieceColor === 1) {
kingGraphics.tint = 0x333333;
}
// Visual feedback for promotion
LK.effects.flashObject(blackShahsade, 0xFFD700, 1000); // Flash gold
// Check if player is in checkmate after promotion
if (isInCheck(1) && !hasValidMoves(1)) {
LK.setScore(1);
LK.showGameOver();
return;
}
// Game continues, don't end
return;
} else {
LK.setScore(1);
LK.showGameOver();
return;
}
}
var inCheck = isInCheck(currentPlayer);
var hasValid = hasValidMoves(currentPlayer);
// Check for checkmate using new function that respects check privileges
if (isCheckmate(currentPlayer)) {
// Checkmate
LK.getSound('checkmate').play();
LK.setScore(1);
LK.showGameOver();
} else if (!hasValid) {
// Stalemate
LK.setScore(0);
LK.showGameOver();
}
}
function createMenu() {
// Create menu container
menuContainer = game.addChild(new Container());
menuContainer.x = 2048 / 2;
menuContainer.y = 2732 / 2;
// Create menu background with Timur portrait
var menuBg = menuContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var titleBox = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 1.0
}));
titleBox.x = 0;
titleBox.y = -1100;
// Create title text
var titleText = new Text2('TAMERLANE CHESS', {
size: 120,
fill: 0x808080,
font: "Algerian,Impact,Arial Black,Tahoma"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -1100;
menuContainer.addChild(titleText);
// Create start button
var startButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
startButton.x = -800;
startButton.y = 550;
var startButtonText = new Text2('START GAME', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
font: "Algerian,Impact,Arial Black,Tahoma"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = 0;
startButton.addChild(startButtonText);
// Create rules button
var rulesButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
rulesButton.x = -800;
rulesButton.y = 700;
var rulesButtonText = new Text2('HOW TO PLAY', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
font: "Algerian,Impact,Arial Black,Tahoma"
});
rulesButtonText.anchor.set(0.5, 0.5);
rulesButtonText.x = 0;
rulesButtonText.y = 0;
rulesButton.addChild(rulesButtonText);
// Add click handlers
startButton.down = function () {
hideMenu();
showVersionSelection();
};
rulesButton.down = function () {
showRulesDialog();
};
// Add hover effects
startButton.move = function () {
startButton.removeChildAt(0); // Remove old background
var hoverBg = startButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
startButton.addChild(startButtonText); // Re-add text on top
};
rulesButton.move = function () {
rulesButton.removeChildAt(0); // Remove old background
var hoverBg = rulesButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
rulesButton.addChild(rulesButtonText); // Re-add text on top
};
}
function hideMenu() {
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
menuVisible = false;
}
function showRulesDialog() {
// Create rules dialog background with Timur portrait
var rulesBg = game.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.8,
alpha: 0.9
}));
rulesBg.x = 2048 / 2;
rulesBg.y = 2732 / 2;
// Create rules text
var rulesText = new Text2('Click anywhere to close', {
size: 45,
fill: 0xFFFFFF
});
rulesText.anchor.set(0.5, 0.5);
rulesText.x = 2048 / 2;
rulesText.y = 2732 / 2;
game.addChild(rulesText);
// Add close handler
rulesBg.down = function () {
rulesBg.destroy();
rulesText.destroy();
};
rulesText.down = function () {
rulesBg.destroy();
rulesText.destroy();
};
}
function initializeGame() {
createBoard();
createPieces();
gameStarted = true;
turnText.visible = true;
moveHistoryTab.visible = true;
}
// Player turn indicator
var turnText = new Text2('White to move', {
size: 80,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
turnText.visible = false; // Hide until game starts
LK.gui.top.addChild(turnText);
// Move history display
var moveHistory = [];
var moveHistoryVisible = false;
var moveHistoryTab = new Text2('Move History', {
size: 50,
fill: 0xFFFFFF
});
moveHistoryTab.anchor.set(0, 0);
moveHistoryTab.x = 50;
moveHistoryTab.y = 150;
moveHistoryTab.visible = false; // Hide until game starts
game.addChild(moveHistoryTab);
var moveHistoryText = new Text2('', {
size: 40,
fill: 0xFFFFFF
});
moveHistoryText.anchor.set(0, 0);
moveHistoryText.x = 50;
moveHistoryText.y = 200;
moveHistoryText.visible = false;
game.addChild(moveHistoryText);
game.down = function (x, y, obj) {
if (!gameStarted || menuVisible || versionSelectionVisible || colorSelectionVisible || aiSelectionVisible) {
return;
}
// Prevent human interaction when it's AI's turn
if (playWithAI && currentPlayer === aiColor) {
return;
}
// Check if move history tab was clicked
if (x >= moveHistoryTab.x && x <= moveHistoryTab.x + 300 && y >= moveHistoryTab.y && y <= moveHistoryTab.y + 60) {
moveHistoryVisible = !moveHistoryVisible;
moveHistoryText.visible = moveHistoryVisible;
if (moveHistoryVisible) {
// Update display when showing
var displayText = 'Move History:\n';
for (var i = 0; i < moveHistory.length; i++) {
if (i % 2 === 0) {
displayText += moveHistory[i] + ' ';
} else {
displayText += moveHistory[i] + '\n';
}
}
moveHistoryText.setText(displayText);
}
return;
}
var coords = getBoardCoordinates(x, y);
if (!isValidSquare(coords.row, coords.col)) {
return;
}
var clickedPiece = getPieceAt(coords.row, coords.col);
if (selectedPiece) {
var validMoves = selectedPiece.getValidMoves();
var isValidMove = false;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === coords.row && validMoves[i].col === coords.col) {
isValidMove = true;
break;
}
}
if (isValidMove) {
var moveData = null;
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].row === coords.row && validMoves[i].col === coords.col) {
moveData = validMoves[i];
break;
}
}
makeMove(selectedPiece, coords.row, coords.col, moveData);
return;
}
}
if (clickedPiece && clickedPiece.pieceColor === currentPlayer) {
// Check if this is a Shah position swap
if (selectedPiece && selectedPiece.pieceType === 'king' && clickedPiece !== selectedPiece) {
var swappablePieces = getSwappablePieces(selectedPiece);
var canSwap = false;
for (var i = 0; i < swappablePieces.length; i++) {
if (swappablePieces[i] === clickedPiece) {
canSwap = true;
break;
}
}
if (canSwap) {
performPositionSwap(selectedPiece, clickedPiece);
return;
}
}
selectPiece(clickedPiece);
} else {
selectPiece(null);
}
};
game.update = function () {
if (!gameStarted && !menuVisible && !versionSelectionVisible && !colorSelectionVisible && !aiSelectionVisible) {
initializeGame();
} else if (menuVisible && !menuContainer) {
createMenu();
} else if (versionSelectionVisible && !versionContainer) {
showVersionSelection();
} else if (colorSelectionVisible && !colorContainer) {
showColorSelection();
} else if (aiSelectionVisible && !aiContainer) {
showAISelection();
}
// Try teleporting pawn of pawns for current player
tryTeleportPawnOfPawns(currentPlayer);
// Enforce saray exit if required
enforceSarayExit(currentPlayer);
var playerText = currentPlayer === 0 ? 'White to move' : 'Black to move';
if (isInCheck(currentPlayer)) {
playerText += ' (Check!)';
}
if (playWithAI && currentPlayer === aiColor) {
playerText += ' (AI thinking...)';
}
turnText.setText(playerText);
// AI move logic
if (playWithAI && gameStarted && currentPlayer === aiColor) {
makeAIMove();
}
};
function enforceSarayExit(playerColor) {
// Check if player is mated due to saray exit requirement
if (checkForcedSarayExitOrMate(playerColor)) {
// Player is mated - end game
LK.setScore(1);
LK.showGameOver();
return;
}
for (var row = 0; row < BOARD_HEIGHT; row++) {
for (var col = 0; col < BOARD_WIDTH; col++) {
var p = getPieceAt(row, col);
if (p && p.pieceColor === playerColor && mustMoveFirst(p, row, col)) {
// Highlight the piece that must exit saray
if (selectedPiece !== p) {
selectPiece(p);
}
return;
}
}
}
}
// List of types the pawn_of_pawns cannot threaten
var EXCLUDED_TYPES = ['camelPawn', 'giraffePawn', 'guardsPawn', 'kingsPawn', 'knightPawn', 'lionsPawn', 'ministersPawn', 'pawnOfTheBull', 'pawnOfTheElephant', 'pawnOfTheLeader', 'pawn', 'pawnOfTheWarMachine', 'viziersPawn', 'king'];
function tryTeleportPawnOfPawns(color) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceType === 'pawnsPawn' && p.pieceColor === color && !p.hasTeleported) {
for (var tr = 0; tr < BOARD_HEIGHT; tr++) {
for (var tc = 0; tc < BOARD_WIDTH; tc++) {
if (!isValidSquare(tr, tc)) {
continue;
} // Skip invalid squares
if (getPieceAt(tr, tc)) {
continue;
} // Square must be empty
if (isSeaSquare(tr, tc)) {
continue;
} // Skip sea squares - pawn's pawn cannot teleport to sea
// Check if target square is a promotion square - pawn's pawn cannot teleport to promotion squares
var opponentPromotionRow = color === 0 ? 1 : BOARD_HEIGHT - 2;
if (tr === opponentPromotionRow) {
continue;
} // Skip promotion squares
var threats = countValidThreats(tr, tc, color);
var blocksImmobilizedEnemy = threatensBlockedEnemy(tr, tc, color);
if (threats >= 2 || blocksImmobilizedEnemy) {
// Teleport the pawn of pawns
p.boardRow = tr;
p.boardCol = tc;
p.hasTeleported = true;
var pos = getBoardPosition(tr, tc);
tween(p, {
x: pos.x,
y: pos.y
}, {
duration: 300
});
console.log('Pawn of Pawns TELEPORTED to (' + tr + ',' + tc + ')');
return;
}
}
}
}
}
}
}
function countValidThreats(row, col, color) {
var count = 0;
var dirs = [[-1, -1], [-1, 1], [1, -1], [1, 1]]; // Diagonals
for (var d = 0; d < dirs.length; d++) {
var dir = dirs[d];
var r = row + dir[0];
var c = col + dir[1];
if (isValidSquare(r, c)) {
var enemy = getPieceAt(r, c);
if (enemy && enemy.pieceColor !== color && EXCLUDED_TYPES.indexOf(enemy.pieceType) === -1) {
// Additional check: pawn of pawns cannot teleport to threaten the king
if (enemy.pieceType === 'king') {
return 0; // Prevent teleportation if it would threaten the king
}
count++;
}
}
}
return count;
}
function threatensBlockedEnemy(row, col, color) {
var dirs = [[-1, -1], [-1, 1], [1, -1], [1, 1]]; // Diagonals
for (var d = 0; d < dirs.length; d++) {
var dir = dirs[d];
var r = row + dir[0];
var c = col + dir[1];
if (isValidSquare(r, c)) {
var enemy = getPieceAt(r, c);
if (enemy && enemy.pieceColor !== color && EXCLUDED_TYPES.indexOf(enemy.pieceType) === -1) {
// Additional check: pawn of pawns cannot teleport to threaten the king
if (enemy.pieceType === 'king') {
return false; // Prevent teleportation if it would threaten the king
}
if (!hasLegalMovesForPiece(enemy, r, c)) {
return true;
}
}
}
}
return false;
}
function hasLegalMovesForPiece(piece, row, col) {
var moves = piece.getRawMoves();
for (var i = 0; i < moves.length; i++) {
if (isValidMoveConsideringCheck(piece, moves[i].row, moves[i].col)) {
return true;
}
}
return false;
}
function hasImmunityPiece(color) {
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].pieceColor === color) {
if (pieces[i].pieceType === 'adventurousKing' || pieces[i].pieceType === 'shahsade') {
return true;
}
}
}
return false;
}
function isPieceUnderThreat(piece) {
return isSquareAttacked(piece.boardRow, piece.boardCol, 1 - piece.pieceColor);
}
function hasCheckPrivilege(color) {
return hasAdventurousKing(color) || hasShahsade(color);
}
function getSwappablePieces(shahPiece) {
var swappablePieces = [];
// Check if Shah position swap has already been used
if (shahPositionSwapUsed[shahPiece.pieceColor]) {
return swappablePieces;
}
// Shah must not be under threat
if (isPieceUnderThreat(shahPiece)) {
return swappablePieces;
}
// Check if king is on promotion square
var isOnPromotionSquare = false;
var opponentPromotionRow = shahPiece.pieceColor === 0 ? 1 : BOARD_HEIGHT - 2;
if (shahPiece.boardRow === opponentPromotionRow) {
isOnPromotionSquare = true;
}
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
// Must be same color but not the Shah itself
if (piece.pieceColor === shahPiece.pieceColor && piece !== shahPiece) {
// If king is on promotion square, exclude all pawn types from swapping
if (isOnPromotionSquare) {
var pawnTypes = ['pawn', 'pawnOfTheWarMachine', 'camelPawn', 'ministersPawn', 'pawnOfTheElephant', 'kingsPawn', 'viziersPawn', 'giraffePawn', 'knightPawn', 'rookPawn', 'pawnOfTheLeader', 'guardsPawn', 'pawnOfTheBull', 'lionsPawn', 'pawnsPawn'];
if (pawnTypes.indexOf(piece.pieceType) !== -1) {
continue; // Skip pawn pieces when king is on promotion square
}
}
// Piece must not be under threat
if (!isPieceUnderThreat(piece)) {
swappablePieces.push(piece);
}
}
}
return swappablePieces;
}
function isCheckmate(color) {
if (hasImmunityPiece(color)) {
console.log(color === 0 ? 'White' : 'Black' + ' has immunity — checkmate ignored.');
return false;
}
return isInCheck(color) && !hasValidMoves(color);
}
function isPalaceSquare(row, col) {
return row === 2 && col === 0 || row === 9 && col === 12;
}
function isSaray(row, col) {
return row === 2 && col === 0 || row === 9 && col === 12;
}
function isInOwnSaray(piece, row, col) {
return piece.pieceColor === 0 && row === 9 && col === 12 || piece.pieceColor === 1 && row === 2 && col === 0;
}
function promoteAdventurousKingToKing(row, col) {
// Removed adventurous king promotion to king when entering own saray
}
function mustExitSarayThisTurn(playerColor) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === playerColor && p.mustExitSaray && isInOwnSaray(p, r, c)) {
return true;
}
}
}
return false;
}
function canEscapeFromSaray(playerColor) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === playerColor && p.mustExitSaray && isInOwnSaray(p, r, c)) {
// Check 8 surrounding squares (like king movement)
var dr = [-1, -1, -1, 0, 1, 1, 1, 0];
var dc = [-1, 0, 1, 1, 1, 0, -1, -1];
for (var i = 0; i < 8; i++) {
var nr = r + dr[i];
var nc = c + dc[i];
if (isValidSquare(nr, nc) && !getPieceAt(nr, nc)) {
return true; // Found an empty escape square
}
}
return false; // Surrounded, no escape
}
}
}
return true; // No forced exit kings found
}
function checkForcedSarayExitOrMate(playerColor) {
if (mustExitSarayThisTurn(playerColor)) {
if (canEscapeFromSaray(playerColor)) {
console.log((playerColor === 0 ? 'White' : 'Black') + ' must move King out of saray this turn.');
return false; // Not mated, but must move
} else {
console.log((playerColor === 0 ? 'White' : 'Black') + ' cannot escape saray — checkmate.');
return true; // Mated
}
}
return false; // No forced exits pending
}
function checkIfKingMustExitNextTurn(playerColor) {
for (var r = 0; r < BOARD_HEIGHT; r++) {
for (var c = 0; c < BOARD_WIDTH; c++) {
var p = getPieceAt(r, c);
if (p && p.pieceColor === playerColor && p.pieceType === 'king' && isInOwnSaray(p, r, c)) {
p.mustExitSaray = true;
console.log((playerColor === 0 ? 'White' : 'Black') + "'s king must exit saray on next turn.");
}
}
}
}
function handleSarayCapture(attacker, row, col) {
if (isSaray(row, col)) {
attacker.mustExitSaray = true;
}
}
function mustMoveFirst(piece, row, col) {
if (!piece || !piece.mustExitSaray) {
return false;
}
if (isSaray(row, col)) {
return true; // still inside → must move out
} else {
piece.mustExitSaray = false; // already out → clear flag
return false;
}
}
function getPlayerPalace(color) {
return color === 1 ? {
row: 2,
col: 0
} : {
row: 9,
col: 12
};
}
function isOpponentPalace(row, col, color) {
if (color === 0) {
return row === 2 && col === 0; // White player, black palace
} else {
return row === 9 && col === 12; // Black player, white palace
}
}
function canEnterPalace(piece, targetRow, targetCol) {
// Coordinates of palace squares
var blackPalace = {
row: 2,
col: 0
}; // Black Palace location
var whitePalace = {
row: 9,
col: 12
}; // White Palace location
if (!piece) {
return false;
}
var type = piece.pieceType;
var color = piece.pieceColor === 0 ? 'white' : 'black';
var isWhiteSaray = targetRow === whitePalace.row && targetCol === whitePalace.col;
var isBlackSaray = targetRow === blackPalace.row && targetCol === blackPalace.col;
var target = getPieceAt(targetRow, targetCol);
var isCapturing = target && target.pieceColor !== piece.pieceColor;
// Normal allowed entry
if (type === 'adventurousKing' || isBlackSaray && color === 'white' && (type === 'king' || type === 'shahsade') || isWhiteSaray && color === 'black' && (type === 'king' || type === 'shahsade')) {
return true;
}
// Special rule: capture king/shahsade/adventurousKing in saray if under threat
if (isCapturing && target && (target.pieceType === 'king' || target.pieceType === 'shahsade' || target.pieceType === 'adventurousKing') && isSaray(targetRow, targetCol)) {
return true;
}
return !isSaray(targetRow, targetCol); // deny entry to saray unless allowed
}
function isPieceInPalace(piece) {
return isPalaceSquare(piece.boardRow, piece.boardCol);
}
var drawOfferPending = false;
var drawOfferPiece = null;
var versionSelectionVisible = false;
var versionContainer = null;
var isTraditionalVersion = false;
var colorSelectionVisible = false;
var colorContainer = null;
var playerColor = 0; // 0 = white, 1 = black
var aiSelectionVisible = false;
var aiContainer = null;
var playWithAI = false;
var aiColor = 1; // AI plays opposite color to player
function showVersionSelection() {
versionSelectionVisible = true;
// Create version selection container
versionContainer = game.addChild(new Container());
versionContainer.x = 2048 / 2;
versionContainer.y = 2732 / 2;
// Create background with Timur portrait
var versionBg = versionContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var versionTitleBox = versionContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.0
}));
versionTitleBox.x = 0;
versionTitleBox.y = -200;
// Create title text
var versionTitleText = new Text2('CHOOSE VERSION', {
size: 100,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
versionTitleText.anchor.set(0.5, 0.5);
versionTitleText.x = 0;
versionTitleText.y = -200;
versionContainer.addChild(versionTitleText);
// Create Traditional Version button
var traditionalButton = versionContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
traditionalButton.x = 0;
traditionalButton.y = -50;
var traditionalButtonText = new Text2('TRADITIONAL VERSION', {
size: 50,
fill: 0xFFFFFF
});
traditionalButtonText.anchor.set(0.5, 0.5);
traditionalButtonText.x = 0;
traditionalButtonText.y = 0;
traditionalButton.addChild(traditionalButtonText);
// Create Modern Version button
var modernButton = versionContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
modernButton.x = 0;
modernButton.y = 100;
var modernButtonText = new Text2('MODERN VERSION', {
size: 50,
fill: 0xFFFFFF
});
modernButtonText.anchor.set(0.5, 0.5);
modernButtonText.x = 0;
modernButtonText.y = 0;
modernButton.addChild(modernButtonText);
// Add click handlers
traditionalButton.down = function () {
isTraditionalVersion = true;
hideVersionSelection();
showColorSelection();
};
modernButton.down = function () {
isTraditionalVersion = false;
hideVersionSelection();
showColorSelection();
};
// Add hover effects
traditionalButton.move = function () {
traditionalButton.removeChildAt(0); // Remove old background
var hoverBg = traditionalButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
traditionalButton.addChild(traditionalButtonText); // Re-add text on top
};
modernButton.move = function () {
modernButton.removeChildAt(0); // Remove old background
var hoverBg = modernButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
modernButton.addChild(modernButtonText); // Re-add text on top
};
}
function hideVersionSelection() {
if (versionContainer) {
versionContainer.destroy();
versionContainer = null;
}
versionSelectionVisible = false;
}
function showColorSelection() {
colorSelectionVisible = true;
// Create color selection container
colorContainer = game.addChild(new Container());
colorContainer.x = 2048 / 2;
colorContainer.y = 2732 / 2;
// Create background with Timur portrait
var colorBg = colorContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var colorTitleBox = colorContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.3,
scaleY: 1.0
}));
colorTitleBox.x = 0;
colorTitleBox.y = -200;
// Create title text
var colorTitleText = new Text2('CHOOSE YOUR COLOR', {
size: 100,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
colorTitleText.anchor.set(0.5, 0.5);
colorTitleText.x = 0;
colorTitleText.y = -200;
colorContainer.addChild(colorTitleText);
// Create White button
var whiteButton = colorContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
whiteButton.x = 0;
whiteButton.y = -50;
var whiteButtonText = new Text2('PLAY AS WHITE', {
size: 50,
fill: 0xFFFFFF
});
whiteButtonText.anchor.set(0.5, 0.5);
whiteButtonText.x = 0;
whiteButtonText.y = 0;
whiteButton.addChild(whiteButtonText);
// Create Black button
var blackButton = colorContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
blackButton.x = 0;
blackButton.y = 100;
var blackButtonText = new Text2('PLAY AS BLACK', {
size: 50,
fill: 0xFFFFFF
});
blackButtonText.anchor.set(0.5, 0.5);
blackButtonText.x = 0;
blackButtonText.y = 0;
blackButton.addChild(blackButtonText);
// Add click handlers
whiteButton.down = function () {
playerColor = 0;
currentPlayer = 0; // White starts first
hideColorSelection();
showAISelection();
};
blackButton.down = function () {
playerColor = 1;
currentPlayer = 0; // White still starts first, but player chose black
hideColorSelection();
showAISelection();
};
// Add hover effects
whiteButton.move = function () {
whiteButton.removeChildAt(0); // Remove old background
var hoverBg = whiteButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
whiteButton.addChild(whiteButtonText); // Re-add text on top
};
blackButton.move = function () {
blackButton.removeChildAt(0); // Remove old background
var hoverBg = blackButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
blackButton.addChild(blackButtonText); // Re-add text on top
};
}
function hideColorSelection() {
if (colorContainer) {
colorContainer.destroy();
colorContainer = null;
}
colorSelectionVisible = false;
}
function showAISelection() {
aiSelectionVisible = true;
aiColor = 1 - playerColor; // AI plays opposite color to player
// Create AI selection container
aiContainer = game.addChild(new Container());
aiContainer.x = 2048 / 2;
aiContainer.y = 2732 / 2;
// Create background with Timur portrait
var aiBg = aiContainer.addChild(LK.getAsset('timurPortrait', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1600,
scaleY: 2732 / 2000,
alpha: 0.85
}));
// Create box around title
var aiTitleBox = aiContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 1.0
}));
aiTitleBox.x = 0;
aiTitleBox.y = -200;
// Create title text
var aiTitleText = new Text2('CHOOSE OPPONENT', {
size: 100,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
aiTitleText.anchor.set(0.5, 0.5);
aiTitleText.x = 0;
aiTitleText.y = -200;
aiContainer.addChild(aiTitleText);
// Create AI button
var aiButton = aiContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
aiButton.x = 0;
aiButton.y = -50;
var aiButtonText = new Text2('PLAY VS AI', {
size: 50,
fill: 0xFFFFFF
});
aiButtonText.anchor.set(0.5, 0.5);
aiButtonText.x = 0;
aiButtonText.y = 0;
aiButton.addChild(aiButtonText);
// Create Human button
var humanButton = aiContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
humanButton.x = 0;
humanButton.y = 100;
var humanButtonText = new Text2('PLAY ALONE', {
size: 50,
fill: 0xFFFFFF
});
humanButtonText.anchor.set(0.5, 0.5);
humanButtonText.x = 0;
humanButtonText.y = 0;
humanButton.addChild(humanButtonText);
// Add click handlers
aiButton.down = function () {
playWithAI = true;
hideAISelection();
initializeGame();
};
humanButton.down = function () {
playWithAI = false;
hideAISelection();
initializeGame();
};
// Add hover effects
aiButton.move = function () {
aiButton.removeChildAt(0); // Remove old background
var hoverBg = aiButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
aiButton.addChild(aiButtonText); // Re-add text on top
};
humanButton.move = function () {
humanButton.removeChildAt(0); // Remove old background
var hoverBg = humanButton.attachAsset('menuButtonHover', {
anchorX: 0.5,
anchorY: 0.5
});
humanButton.addChild(humanButtonText); // Re-add text on top
};
}
function hideAISelection() {
if (aiContainer) {
aiContainer.destroy();
aiContainer = null;
}
aiSelectionVisible = false;
}
function offerDraw(piece) {
// Check if piece is in opponent's palace
if (isOpponentPalace(piece.boardRow, piece.boardCol, piece.pieceColor)) {
var playerName = piece.pieceColor === 0 ? 'White' : 'Black';
drawOfferPending = true;
drawOfferPiece = piece;
// Create draw offer dialog
showDrawOfferDialog(playerName);
return true; // Game is paused for draw offer
}
return false;
}
function showDrawOfferDialog(playerName) {
// Create dialog background
var dialogBg = game.addChild(LK.getAsset('darkSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 3,
alpha: 0.9
}));
dialogBg.x = 2048 / 2;
dialogBg.y = 2732 / 2;
// Create dialog text
var dialogText = new Text2(playerName + ' offers a draw.\nDo you accept?', {
size: 60,
fill: 0xFFFFFF
});
dialogText.anchor.set(0.5, 0.5);
dialogText.x = 2048 / 2;
dialogText.y = 2732 / 2 - 100;
game.addChild(dialogText);
// Create Yes button
var yesButton = game.addChild(LK.getAsset('lightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1
}));
yesButton.x = 2048 / 2 - 200;
yesButton.y = 2732 / 2 + 100;
var yesText = new Text2('Yes', {
size: 50,
fill: 0x000000
});
yesText.anchor.set(0.5, 0.5);
yesText.x = yesButton.x;
yesText.y = yesButton.y;
game.addChild(yesText);
// Create No button
var noButton = game.addChild(LK.getAsset('lightSquare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1
}));
noButton.x = 2048 / 2 + 200;
noButton.y = 2732 / 2 + 100;
var noText = new Text2('No', {
size: 50,
fill: 0x000000
});
noText.anchor.set(0.5, 0.5);
noText.x = noButton.x;
noText.y = noButton.y;
game.addChild(noText);
// Add click handlers
yesButton.down = function () {
// Accept draw
LK.setScore(0); // Draw
LK.showGameOver();
};
noButton.down = function () {
// Decline draw - clean up dialog and continue game
dialogBg.destroy();
dialogText.destroy();
yesButton.destroy();
yesText.destroy();
noButton.destroy();
noText.destroy();
drawOfferPending = false;
drawOfferPiece = null;
};
}
function performPositionSwap(shahPiece, targetPiece) {
// Store original positions
var shahRow = shahPiece.boardRow;
var shahCol = shahPiece.boardCol;
var targetRow = targetPiece.boardRow;
var targetCol = targetPiece.boardCol;
// Swap positions
shahPiece.boardRow = targetRow;
shahPiece.boardCol = targetCol;
targetPiece.boardRow = shahRow;
targetPiece.boardCol = shahCol;
// Animate the swap
var shahPos = getBoardPosition(targetRow, targetCol);
var targetPos = getBoardPosition(shahRow, shahCol);
tween(shahPiece, {
x: shahPos.x,
y: shahPos.y
}, {
duration: 300
});
tween(targetPiece, {
x: targetPos.x,
y: targetPos.y
}, {
duration: 300
});
// Mark that position swap has been used
shahPositionSwapUsed[shahPiece.pieceColor] = true;
// Record position swap in move history
var notation = 'K-' + pieceToNotation(targetPiece.pieceType) + ' swap';
var moveNumber = Math.floor(moveHistory.length / 2) + 1;
var colorPrefix = currentPlayer === 0 ? moveNumber + '.' : '';
moveHistory.push(colorPrefix + notation);
// Update move history display only if visible
if (moveHistoryVisible) {
var displayText = 'Move History:\n';
for (var i = 0; i < moveHistory.length; i++) {
if (i % 2 === 0) {
displayText += moveHistory[i] + ' ';
} else {
displayText += moveHistory[i] + '\n';
}
}
moveHistoryText.setText(displayText);
}
// Play move sound
LK.getSound('move').play();
// Change turn
currentPlayer = 1 - currentPlayer;
selectPiece(null);
clearSwapHighlights();
}