User prompt
Remove the ability of the Sea Monster to move 1 square diagonally while moving in the sea, and do not touch the way it eats pieces on the board
User prompt
The Sea Monster cannot jump over squares while moving in the sea
User prompt
Remove the sea monster's movement restriction in the sea, but restrict its eating movements inside the board
User prompt
Add Sea monster cannot move inside the board in any way, it can only eat the pieces on the board. It can move 3 squares in any direction on the sea squares. Do not remove or change any moves other than those specified
User prompt
Add Sea monster cannot move on the board in any way, it can only eat the pieces on the board. Do not remove or change any moves other than those specified
User prompt
Add Sea monster cannot move on the board in any way, it can only eat the pieces on the board
User prompt
Add Sea monster cannot move onto the board in any way
User prompt
Add Sea monster can move 3 squares in any direction under any circumstances, cannot move onto the board, do not change anything except for the specified moves
User prompt
The sea monster can move three squares in any direction under any circumstances, but cannot move onto the board
User prompt
If the Sea Monster is on the lower of the board, it can only eat 3 squares to the up or 1 square diagonally
User prompt
If the Sea Monster is on the upper of the board, it can only eat 3 squares to the up or 1 square diagonally
User prompt
If the Sea Monster is on the lower of the board, it can only eat 3 squares to the down or 1 square diagonally
User prompt
If the Sea Monster is on the right lower of the board, it can only eat 3 squares to the down or 1 square diagonally
User prompt
If the Sea Monster is on the right region of the board, it can only eat 3 squares to the left or 1 square diagonally
User prompt
If the Sea Monster is on the left region of the board, it can only eat 3 squares to the right or 1 square diagonally
User prompt
Define c10, d10, e10, f10, g10, h10, i10, j10, k10, and l10 as the lower region of the board
User prompt
If the Sea Monster is on the upper lower of the board, it can only eat 3 squares to the down or 1 square diagonally
User prompt
If the Sea Monster is on the right lower of the board, it can only eat 3 squares to the up or 1 square diagonally
User prompt
If the Sea Monster is on the right region of the board, it can only eat 3 squares to the left or 1 square diagonally
User prompt
If the Sea Monster is on the left region of the board, it can only eat 3 squares to the right or 1 square diagonally
User prompt
Define c9, d9, e9, f9, g9, h9, i9, j9, k9, and l9 as the lower region of the board
User prompt
Define c1, d1, e1, f1, g1, h1, i1, j1, k1, and l1 as the upper region of the board
User prompt
Define rows M and N of the board as the right region of the board
User prompt
Add = Region definition system: To be used for operations such as “In the future, a stone can make move X in region X.” Define rows A and B of the board as the left region of the board
/****
* 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':
// Sea Monster can move up to 3 squares orthogonally and 1 square diagonally
// When capturing, it doesn't move from its original position
// Special restriction: If in left region, can only eat 3 squares to the right or 1 square diagonally
// Special restriction: If in right region, can only eat 3 squares to the left or 1 square diagonally
var isInLeftRegion = isInRegion(self.boardRow, self.boardCol, 'left');
var isInRightRegion = isInRegion(self.boardRow, self.boardCol, 'right');
var isInRightLowerRegion = isInRightRegion && self.boardRow === 9 && self.boardCol >= 2 && self.boardCol <= 11;
var orthogonalDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
var diagonalDirections = [{
dr: 1,
dc: 1
}, {
dr: 1,
dc: -1
}, {
dr: -1,
dc: 1
}, {
dr: -1,
dc: -1
}];
// Handle orthogonal movement (up to 3 squares)
for (var i = 0; i < orthogonalDirections.length; i++) {
var dir = orthogonalDirections[i];
// If sea monster is in left region, only allow right movement (dc: 1)
if (isInLeftRegion && !(dir.dr === 0 && dir.dc === 1)) {
continue; // Skip all directions except right when in left region
}
// If sea monster is in right lower region, only allow up movement (dr: -1)
if (isInRightLowerRegion && !(dir.dr === -1 && dir.dc === 0)) {
continue; // Skip all directions except up when in right lower region
}
// If sea monster is in right region (but not right lower), only allow left movement (dc: -1)
if (isInRightRegion && !isInRightLowerRegion && !(dir.dr === 0 && dir.dc === -1)) {
continue; // Skip all directions except left when in right region
}
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) {
// Sea monster captures without moving - mark as remote capture
moves.push({
row: newRow,
col: newCol,
isRemoteCapture: true
});
}
break;
}
// Only add regular moves to empty squares
moves.push({
row: newRow,
col: newCol
});
}
}
// Handle diagonal movement (maximum 1 square)
for (var i = 0; i < diagonalDirections.length; i++) {
var dir = diagonalDirections[i];
var dist = 1; // Only 1 square for diagonal movement
var newRow = self.boardRow + dir.dr * dist;
var newCol = self.boardCol + dir.dc * dist;
if (!isValidSquare(newRow, newCol)) {
continue;
}
var piece = getPieceAt(newRow, newCol);
if (piece) {
if (piece.pieceColor !== self.pieceColor) {
// Sea monster captures without moving - mark as remote capture
moves.push({
row: newRow,
col: newCol,
isRemoteCapture: true
});
}
} else {
// Only add regular moves to empty squares
moves.push({
row: newRow,
col: newCol
});
}
}
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 c9, d9, e9, f9, g9, h9, i9, j9, k9, and l9'
}
};
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 9 (c9, d9, etc.) for lower region
if (region.cols && row === 9 && 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,
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 = -400;
// Create title text
var titleText = new Text2('TAMERLANE CHESS', {
size: 120,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 8
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -400;
menuContainer.addChild(titleText);
// Create start button
var startButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
startButton.x = 0;
startButton.y = 0;
var startButtonText = new Text2('START GAME', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
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
}));
rulesButton.x = 0;
rulesButton.y = 150;
var rulesButtonText = new Text2('HOW TO PLAY', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
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;
}
// Player turn indicator
var turnText = new Text2('White to move', {
size: 80,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
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;
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,
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,
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,
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();
} ===================================================================
--- original.js
+++ change.js
@@ -1978,8 +1978,9 @@
// Special restriction: If in left region, can only eat 3 squares to the right or 1 square diagonally
// Special restriction: If in right region, can only eat 3 squares to the left or 1 square diagonally
var isInLeftRegion = isInRegion(self.boardRow, self.boardCol, 'left');
var isInRightRegion = isInRegion(self.boardRow, self.boardCol, 'right');
+ var isInRightLowerRegion = isInRightRegion && self.boardRow === 9 && self.boardCol >= 2 && self.boardCol <= 11;
var orthogonalDirections = [{
dr: 0,
dc: 1
}, {
@@ -2011,10 +2012,14 @@
// If sea monster is in left region, only allow right movement (dc: 1)
if (isInLeftRegion && !(dir.dr === 0 && dir.dc === 1)) {
continue; // Skip all directions except right when in left region
}
- // If sea monster is in right region, only allow left movement (dc: -1)
- if (isInRightRegion && !(dir.dr === 0 && dir.dc === -1)) {
+ // If sea monster is in right lower region, only allow up movement (dr: -1)
+ if (isInRightLowerRegion && !(dir.dr === -1 && dir.dc === 0)) {
+ continue; // Skip all directions except up when in right lower region
+ }
+ // If sea monster is in right region (but not right lower), only allow left movement (dc: -1)
+ if (isInRightRegion && !isInRightLowerRegion && !(dir.dr === 0 && dir.dc === -1)) {
continue; // Skip all directions except left when in right region
}
for (var dist = 1; dist <= 3; dist++) {
var newRow = self.boardRow + dir.dr * dist;