User prompt
public List
User prompt
if (SeaMonster.canCapture(seaRow, seaCol, targetRow, targetCol, rows, cols, board)) { // Taşı yiyoruz board[targetRow][targetCol] = null; // SeaMonster aynı yerde kalıyor, hareket etmiyor }
User prompt
import java.util.ArrayList;
import java.util.List;
public class SeaMonster {
private int x; // row
private int y; // col
private String positionType; // "LEFT", "RIGHT", "TOP", "BOTTOM"
public SeaMonster(int x, int y, String positionType) {
this.x = x;
this.y = y;
this.positionType = positionType.toUpperCase();
}
// Validates that the Sea Monster is in a water cell
public boolean isInWater(boolean[][] boardWaterMap) {
return boardWaterMap[x][y];
}
// Calculates possible movement within water tiles (up to 3 tiles in straight directions)
public List
User prompt
If the pieces can capture the sea monster, they can enter the sea
User prompt
abstract class Piece { protected int row; protected int col; public Piece(int row, int col) { this.row = row; this.col = col; } public abstract String getType(); // General move method // Returns true if move is successful public boolean moveTo(int targetRow, int targetCol, Board board) { // Simple check: target square must be empty if (!board.isEmpty(targetRow, targetCol)) return false; // Here you would check piece-specific move rules // For simplicity, just move if empty this.row = targetRow; this.col = targetCol; return true; } // Capture sea monster method // The piece can move onto the sea monster's position (capture), // but after capturing, the piece's position does NOT change. public boolean captureSeaMonster(SeaMonster seaMonster, Board board) { String type = getType().toLowerCase(); if (!(type.equals("bishop") || type.equals("siegeTower") || type.equals("king"))) { return false; // Only these pieces can capture the sea monster } if (!seaMonster.isInSea()) { return false; // Sea monster must be in the sea } int seaRow = seaMonster.getRow(); int seaCol = seaMonster.getCol(); // Check if this piece can move onto the sea monster's square according to move rules if (!canMoveTo(seaRow, seaCol, board)) { return false; // Cannot move there, so cannot capture } // Remove the sea monster from the board board.removePiece(seaMonster); // IMPORTANT: The piece's position does NOT change (the piece does not actually move) // So row and col stay the same return true; // Capture successful } // Abstract method to check if piece can move to target square according to game rules protected abstract boolean canMoveTo(int targetRow, int targetCol, Board board); public int getRow() { return row; } public int getCol() { return col; } } // SeaMonster class class SeaMonster { private int row; private int col; private boolean inSea; public SeaMonster(int row, int col, boolean inSea) { this.row = row; this.col = col; this.inSea = inSea; } public int getRow() { return row; } public int getCol() { return col; } public boolean isInSea() { return inSea; } } // Board class for managing pieces class Board { private Piece[][] board; public Board(int size) { board = new Piece[size][size]; } public boolean isEmpty(int row, int col) { return board[row][col] == null; } public void removePiece(SeaMonster seaMonster) { int r = seaMonster.getRow(); int c = seaMonster.getCol(); board[r][c] = null; // You may want to update seaMonster state here as well } } // Example piece implementations: class Bishop extends Piece { public Bishop(int row, int col) { super(row, col); } @Override public String getType() { return "bishop"; } @Override protected boolean canMoveTo(int targetRow, int targetCol, Board board) { // Simple diagonal move check (no obstacle checking here) return Math.abs(targetRow - row) == Math.abs(targetCol - col); } } class SiegeTower extends Piece { public SiegeTower(int row, int col) { super(row, col); } @Override public String getType() { return "siegeTower"; } @Override protected boolean canMoveTo(int targetRow, int targetCol, Board board) { // Simple horizontal or vertical move check return (targetRow == row || targetCol == col); } } class King extends Piece { public King(int row, int col) { super(row, col); } @Override public String getType() { return "king"; } @Override protected boolean canMoveTo(int targetRow, int targetCol, Board board) { int dRow = Math.abs(targetRow - row); int dCol = Math.abs(targetCol - col); return dRow <= 1 && dCol <= 1; } }
User prompt
Make the texture of the Sea Monster's capture square orange ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
// Base class for all pieces abstract class Piece { protected int row; protected int col; public Piece(int row, int col) { this.row = row; this.col = col; } public abstract String getType(); // Check if piece can capture sea monster at (seaRow, seaCol) without moving public boolean canCaptureSeaMonster(SeaMonster seaMonster) { // Only bishop, siegeTower, and king can capture String type = getType().toLowerCase(); if (!(type.equals("bishop") || type.equals("siegeTower") || type.equals("king"))) { return false; } // Piece does not move: it captures from its own position // Check if the sea monster is in range of capture from piece's position // (capture logic is simplified; customize according to your game rules) // Here, as an example, assume the piece can capture if sea monster is adjacent (including diagonals) int dRow = Math.abs(seaMonster.getRow() - this.row); int dCol = Math.abs(seaMonster.getCol() - this.col); return (dRow <= 1 && dCol <= 1); } } // SeaMonster class class SeaMonster { private int row; private int col; private boolean inSea; public SeaMonster(int row, int col, boolean inSea) { this.row = row; this.col = col; this.inSea = inSea; } public int getRow() { return row; } public int getCol() { return col; } public boolean isInSea() { return inSea; } } // Example subclasses class Bishop extends Piece { public Bishop(int row, int col) { super(row, col); } @Override public String getType() { return "bishop"; } } class SiegeTower extends Piece { public SiegeTower(int row, int col) { super(row, col); } @Override public String getType() { return "siegeTower"; } } class King extends Piece { public King(int row, int col) { super(row, col); } @Override public String getType() { return "king"; } } class OtherPiece extends Piece { public OtherPiece(int row, int col) { super(row, col); } @Override public String getType() { return "pawn"; } } // Test method public class Game { public static void main(String[] args) { SeaMonster seaMonster = new SeaMonster(4, 4, true); // sea monster at (4,4), in sea Bishop bishop = new Bishop(3, 3); SiegeTower siegeTower = new SiegeTower(5, 4); King king = new King(4, 3); OtherPiece pawn = new OtherPiece(4, 5); System.out.println("Bishop can capture: " + bishop.canCaptureSeaMonster(seaMonster)); System.out.println("SiegeTower can capture: " + siegeTower.canCaptureSeaMonster(seaMonster)); System.out.println("King can capture: " + king.canCaptureSeaMonster(seaMonster)); System.out.println("Pawn can capture: " + pawn.canCaptureSeaMonster(seaMonster)); } }
User prompt
public static boolean canCaptureSeaMonster(int attackerRow, int attackerCol, int monsterRow, int monsterCol, Piece attacker) { if (!attacker.isEnemy) return false; int dr = Math.abs(monsterRow - attackerRow); int dc = Math.abs(monsterCol - attackerCol); switch (attacker.type) { case "bishop": // Bishop attacks diagonally without moving return dr == dc; case "siegeTower": // SiegeTower attacks in straight lines without moving return (dr == 0 || dc == 0); case "king": // King attacks adjacent squares (1-square range) return dr <= 1 && dc <= 1; default: return false; // Other pieces cannot capture the SeaMonster } }
User prompt
public static boolean canCapture(int sx, int sy, int tx, int ty, Piece[][] board) { if (!isInSea(sx)) return false; if (tx < 0 || tx >= board.length || ty < 0 || ty >= board[0].length) return false; Piece target = board[tx][ty]; if (target == null || !target.isEnemy) return false; // Protected pieces: SeaMonster cannot capture these if (target.type.equals("king") || target.type.equals("rook") || target.type.equals("bishop") || // 🠔 Vanguard changed to Bishop target.type.equals("siegeTower") || isProtected(board, tx, ty)) return false; int dr = Math.abs(tx - sx); int dc = Math.abs(ty - sy); // ✅ Straight 3-square attack (horizontal or vertical) if ((dr == 0 && dc == 3) || (dc == 0 && dr == 3)) return true; // ✅ Diagonal 1-square attack if (dr == 1 && dc == 1) return true; // ❌ L-shaped and Long L attacks removed return false; }
User prompt
public static boolean canCapture(int sx, int sy, int tx, int ty, Piece[][] board) { if (!isInSea(sx)) return false; if (tx < 0 || tx >= board.length || ty < 0 || ty >= board[0].length) return false; Piece target = board[tx][ty]; if (target == null || !target.isEnemy) return false; // Korunan taşları ele geçirme engeli if (target.type.equals("king") || target.type.equals("rook") || target.type.equals("vanguard") || target.type.equals("siegeTower") || isProtected(board, tx, ty)) return false; int dr = Math.abs(tx - sx); int dc = Math.abs(ty - sy); // ✅ Sadece 3 kare düz (yatay ya da dikey) if ((dr == 0 && dc == 3) || (dc == 0 && dr == 3)) return true; // ✅ Sadece 1 kare çapraz if (dr == 1 && dc == 1) return true; // ❌ L (2+1) ve ❌ Uzun L (3+1) kaldırıldı return false; }
User prompt
class Piece { String type; // "pawn", "king", "rook", "siegeTower", "vanguard", "seaMonster", etc. boolean isEnemy; public Piece(String type, boolean isEnemy) { this.type = type; this.isEnemy = isEnemy; } } public class SeaMonster { static final int SEA_ROW_START = 6; static final int SEA_ROW_END = 7; public static boolean isInSea(int row) { return row >= SEA_ROW_START && row <= SEA_ROW_END; } public static boolean isProtected(Piece[][] board, int row, int col) { int rows = board.length; int cols = board[0].length; int[][] directions = { {-1, 0}, {1, 0}, {0, -1}, {0, 1}, // rook {-1, -1}, {-1, 1}, {1, -1}, {1, 1}, // king {-2, -1}, {-1, -2}, {-2, 1}, {-1, 2}, {1, -2}, {2, -1}, {1, 2}, {2, 1} // knight-style }; for (int[] dir : directions) { int r = row + dir[0]; int c = col + dir[1]; if (r >= 0 && r < rows && c >= 0 && c < cols && board[r][c] != null) { Piece p = board[r][c]; if (!p.isEnemy) continue; // SeaMonster only checks enemy protection if (p.type.equals("rook") && (dir[0] == 0 || dir[1] == 0)) return true; if (p.type.equals("king") && Math.abs(dir[0]) <= 1 && Math.abs(dir[1]) <= 1) return true; if (p.type.equals("vanguard") || p.type.equals("siegeTower")) return true; } } return false; } public static boolean canMoveTo(int row, int col, Piece[][] board) { if (!isInSea(row)) return false; // Cannot move to sea square threatened by a rook int[][] rookDirs = { {-1,0}, {1,0}, {0,-1}, {0,1} }; for (int[] dir : rookDirs) { for (int i = 1; i <= 3; i++) { int r = row + dir[0] * i; int c = col + dir[1] * i; if (r < 0 || r >= board.length || c < 0 || c >= board[0].length) break; Piece p = board[r][c]; if (p != null) { if (p.isEnemy && p.type.equals("rook")) { return false; } else break; } } } return true; } public static boolean canCapture(int sx, int sy, int tx, int ty, Piece[][] board) { if (!isInSea(sx)) return false; if (tx < 0 || tx >= board.length || ty < 0 || ty >= board[0].length) return false; Piece target = board[tx][ty]; if (target == null || !target.isEnemy) return false; // Cannot capture protected pieces if (target.type.equals("king") || target.type.equals("rook") || target.type.equals("vanguard") || target.type.equals("siegeTower") || isProtected(board, tx, ty)) return false; int dr = Math.abs(tx - sx); int dc = Math.abs(ty - sy); // Straight (row or col) 1-3 if ((dr == 0 && dc >= 1 && dc <= 3) || (dc == 0 && dr >= 1 && dr <= 3)) return true; // Diagonal 1 if (dr == 1 && dc == 1) return true; // L-shape if ((dr == 2 && dc == 1) || (dr == 1 && dc == 2)) return true; // Long L (3 + 1) if ((dr == 3 && dc == 1) || (dr == 1 && dc == 3)) return true; return false; } public static void attemptCapture(int sx, int sy, int tx, int ty, Piece[][] board) { if (canCapture(sx, sy, tx, ty, board)) { board[tx][ty] = null; // Captures the piece System.out.println("SeaMonster captured enemy at (" + tx + ", " + ty + ")"); } else { System.out.println("Capture not allowed."); } } public static void main(String[] args) { int rows = 8, cols = 8; Piece[][] board = new Piece[rows][cols]; // SeaMonster placed in sea area int seaX = 6, seaY = 4; // Enemy in valid position (3 right) board[6][7] = new Piece("pawn", true); // Enemy rook protecting the square (blocks capture) board[6][6] = new Piece("rook", true); // Attempt to capture attemptCapture(seaX, seaY, 6, 7, board); // Attempt to move into rook's threatened square if (canMoveTo(6, 7, board)) { System.out.println("SeaMonster can move to (6, 7)"); } else { System.out.println("SeaMonster cannot move to (6, 7) — rook threat"); } } }
User prompt
Remove the Sea Monster's ability to draw a long L
User prompt
if (SeaMonster.canCapture(seaRow, seaCol, targetRow, targetCol, rows, cols, board)) { // Taşı yiyoruz board[targetRow][targetCol] = null; // SeaMonster aynı yerde kalıyor, hareket etmiyor }
User prompt
public class SeaMonsterGame { static class Piece { boolean isEnemy; public Piece(boolean isEnemy) { this.isEnemy = isEnemy; } } static class SeaMonster { // Define sea area static final int SEA_ROW_START = 6; static final int SEA_ROW_END = 7; static final int SEA_COL_START = 0; static final int SEA_COL_END = 9; // Check if position is in the sea public static boolean isInSea(int row, int col) { return row >= SEA_ROW_START && row <= SEA_ROW_END && col >= SEA_COL_START && col <= SEA_COL_END; } // Movement: Up to 3 squares in any straight direction within the sea public static boolean canMove(int fromRow, int fromCol, int toRow, int toCol) { if (!isInSea(fromRow, fromCol) || !isInSea(toRow, toCol)) return false; int rowDiff = Math.abs(toRow - fromRow); int colDiff = Math.abs(toCol - fromCol); // Allow movement only in straight lines (no diagonals), up to 3 squares if ((rowDiff == 0 && colDiff > 0 && colDiff <= 3) || (colDiff == 0 && rowDiff > 0 && rowDiff <= 3)) { return true; } return false; } // Attack logic (SeaMonster does not move) public static boolean canCapture(int monsterRow, int monsterCol, int targetRow, int targetCol, int boardRows, int boardCols, Piece[][] board) { if (!isInSea(monsterRow, monsterCol)) return false; if (targetRow < 0 || targetRow >= boardRows || targetCol < 0 || targetCol >= boardCols) return false; Piece target = board[targetRow][targetCol]; if (target == null || !target.isEnemy) return false; int rowDiff = targetRow - monsterRow; int colDiff = targetCol - monsterCol; int absRow = Math.abs(rowDiff); int absCol = Math.abs(colDiff); // 1. Straight 3-square attacks if ((absRow == 3 && colDiff == 0) || (absCol == 3 && rowDiff == 0)) return true; // 2. 1-square diagonal if (absRow == 1 && absCol == 1) return true; // 3. Normal L-shape if ((absRow == 2 && absCol == 1) || (absRow == 1 && absCol == 2)) return true; // 4. Long L-shape (3+1) if ((absRow == 3 && absCol == 1) || (absRow == 1 && absCol == 3)) return true; return false; } } public static void main(String[] args) { int rows = 10; int cols = 10; Piece[][] board = new Piece[rows][cols]; int seaRow = 6; int seaCol = 4; // Place some enemy pieces in attackable positions board[3][4] = new Piece(true); // 3 up board[6][1] = new Piece(true); // 3 left board[7][7] = new Piece(true); // diagonal board[4][5] = new Piece(true); // L-shape board[3][5] = new Piece(true); // Long L board[9][3] = new Piece(true); // Outside attack range (won't be hit) // Test movement System.out.println("SeaMonster can move from (" + seaRow + ", " + seaCol + ") to:"); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (SeaMonster.canMove(seaRow, seaCol, r, c)) { System.out.println(" → (" + r + ", " + c + ")"); } } } // Test attack System.out.println("\nSeaMonster can attack from (" + seaRow + ", " + seaCol + "):"); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (SeaMonster.canCapture(seaRow, seaCol, r, c, rows, cols, board)) { System.out.println(" → (" + r + ", " + c + ")"); } } } } }
User prompt
Add Sea Monster, can move 3 squares left and right
User prompt
public class SeaMonsterGame { static class Piece { boolean isEnemy; public Piece(boolean isEnemy) { this.isEnemy = isEnemy; } } static class SeaMonster { // Define sea area (example: columns 0 to 9, rows 6–7) static final int SEA_COL_START = 0; static final int SEA_COL_END = 9; static final int SEA_ROW_START = 6; static final int SEA_ROW_END = 9; public static boolean isInSea(int row, int col) { return row >= SEA_ROW_START && row <= SEA_ROW_END && col >= SEA_COL_START && col <= SEA_COL_END; } // Movement: only vertical (up to 3 squares), no left/right public static boolean canMoveInSea(int fromRow, int toRow, int col) { int rowDiff = Math.abs(toRow - fromRow); return isInSea(toRow, col) && rowDiff <= 3; } // Attack logic (SeaMonster does not move) public static boolean canCapture(int monsterRow, int monsterCol, int targetRow, int targetCol, int boardRows, int boardCols, Piece[][] board) { if (!isInSea(monsterRow, monsterCol)) return false; if (targetRow < 0 || targetRow >= boardRows || targetCol < 0 || targetCol >= boardCols) return false; Piece target = board[targetRow][targetCol]; if (target == null || !target.isEnemy) return false; int rowDiff = targetRow - monsterRow; int colDiff = targetCol - monsterCol; int absRow = Math.abs(rowDiff); int absCol = Math.abs(colDiff); // 1. 3 squares straight (row or col) if ((absRow == 3 && absCol == 0) || (absCol == 3 && absRow == 0)) return true; // 2. 1-square diagonal if (absRow == 1 && absCol == 1) return true; // 3. Regular L-shape if ((absRow == 2 && absCol == 1) || (absRow == 1 && absCol == 2)) return true; // 4. Long L-shape (3 + 1) if ((absRow == 3 && absCol == 1) || (absRow == 1 && absCol == 3)) return true; return false; } } public static void main(String[] args) { int rows = 10; int cols = 10; Piece[][] board = new Piece[rows][cols]; // SeaMonster starts at (6, 4) in the sea int seaRow = 6; int seaCol = 4; // Place some enemy pieces for testing board[3][4] = new Piece(true); // vertical 3 up board[6][7] = new Piece(true); // diagonal board[4][6] = new Piece(true); // L-shape board[3][5] = new Piece(true); // long L board[9][3] = new Piece(true); // long L down board[8][4] = new Piece(true); // vertical move position // Movement test System.out.println("SeaMonster can move from (" + seaRow + ", " + seaCol + ") to:"); for (int r = 0; r < rows; r++) { if (SeaMonster.canMoveInSea(seaRow, r, seaCol)) { System.out.println(" → (" + r + ", " + seaCol + ")"); } } // Attack test System.out.println("\nSeaMonster can capture:"); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (SeaMonster.canCapture(seaRow, seaCol, r, c, rows, cols, board)) { System.out.println(" → (" + r + ", " + c + ")"); } } } } }
User prompt
Add Sea Monster, captures chess pieces without moving them
User prompt
Add Sea Monster, cannot eat friendly chess pieces
User prompt
Add Sea Monster, cannot eat friendly chess pieces and can never leave the sea
User prompt
Add Sea Monster, you can jump over chess pieces, whether they are friends or enemies
User prompt
The Sea Monster captures chess pieces without moving them himself.
User prompt
make 4
User prompt
Add Sea Monster moves 3 squares in the sea
User prompt
Add Sea Monster Can attack 3 lines
User prompt
public class SeaMonsterGame { static class Piece { boolean isEnemy; public Piece(boolean isEnemy) { this.isEnemy = isEnemy; } } public static class SeaMonster { static final int SEA_ROW_START = 6; static final int SEA_ROW_END = 7; public static boolean isInSea(int row) { return row >= SEA_ROW_START && row <= SEA_ROW_END; } /** * SeaMonster can attack: * - Enemy pieces only * - Jumps over all pieces * - Doesn't move * - Valid targets: * 1. Straight lines (up to 3 squares) * 2. 1-square diagonals * 3. Wide horizontal L-shapes (2 horiz + 1 vert) * 4. Special left-3 zone * 5. NEW: 3 rows then 1 column (tall L-shape) */ public static boolean canCapture(int monsterRow, int monsterCol, int targetRow, int targetCol, int boardRows, int boardCols, Piece[][] board) { if (!isInSea(monsterRow)) return false; if (targetRow < 0 || targetRow >= boardRows || targetCol < 0 || targetCol >= boardCols) return false; Piece targetPiece = board[targetRow][targetCol]; if (targetPiece == null || !targetPiece.isEnemy) return false; int rowDiff = targetRow - monsterRow; int colDiff = targetCol - monsterCol; int absRow = Math.abs(rowDiff); int absCol = Math.abs(colDiff); // 1-square diagonal if (absRow == 1 && absCol == 1) return true; // Straight lines (up to 3 squares) if ((absRow == 0 && absCol >= 1 && absCol <= 3) || (absCol == 0 && absRow >= 1 && absRow <= 3)) { return true; } // Wide L-shape: 2 horizontal + 1 vertical if (absCol == 2 && absRow == 1) return true; // Special left-3 zone if (targetCol == monsterCol - 3 && (targetRow == monsterRow || targetRow == monsterRow - 1 || targetRow == monsterRow + 1)) { return true; } // NEW: Tall L-shape (3 rows + 1 column) if (absRow == 3 && absCol == 1) return true; return false; } } public static void main(String[] args) { int rows = 10; int cols = 8; Piece[][] board = new Piece[rows][cols]; int monsterRow = 6; int monsterCol = 4; // Enemy pieces in tall L shape board[3][3] = new Piece(true); // up 3, left 1 board[3][5] = new Piece(true); // up 3, right 1 board[9][3] = new Piece(true); // down 3, left 1 board[9][5] = new Piece(true); // down 3, right 1 // Valid previous targets board[5][5] = new Piece(true); // diagonal board[6][1] = new Piece(true); // 3 left board[5][1] = new Piece(true); // 3 left + up board[7][6] = new Piece(true); // L-shape right // Friendly pieces (should NOT be captured) board[5][2] = new Piece(false); board[9][4] = new Piece(false); System.out.println("SeaMonster at (" + monsterRow + ", " + monsterCol + ") can capture:"); for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (row == monsterRow && col == monsterCol) continue; if (SeaMonster.canCapture(monsterRow, monsterCol, row, col, rows, cols, board)) { System.out.println(" → Enemy at (" + row + ", " + col + ")"); } } } } }
/****
* 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
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 === '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)
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':
// 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':
// 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)
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
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 === '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
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 === '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
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 === '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
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 === '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
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)
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
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 === '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
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 === '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
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 === '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
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 === '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
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 === '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
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 === '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 only move within sea areas
if (!isSeaSquare(self.boardRow, self.boardCol)) {
// Sea Monster not in sea - no movement allowed
break;
}
// Sea Monster moves up to 3 squares in straight lines within sea
var straightDirections = [{
dr: 0,
dc: 1
}, {
dr: 0,
dc: -1
}, {
dr: 1,
dc: 0
}, {
dr: -1,
dc: 0
}];
for (var i = 0; i < straightDirections.length; i++) {
var dir = straightDirections[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) || !isSeaSquare(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
});
}
}
// Sea Monster can attack in multiple patterns from sea
// 1. Straight 3-square attacks
for (var i = 0; i < straightDirections.length; i++) {
var dir = straightDirections[i];
var attackRow = self.boardRow + dir.dr * 3;
var attackCol = self.boardCol + dir.dc * 3;
if (isValidSquare(attackRow, attackCol)) {
var piece = getPieceAt(attackRow, attackCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: attackRow,
col: attackCol,
isSeaMonsterAttack: true // Sea monster attack, doesn't move
});
}
}
}
// 2. 1-square diagonal attacks
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 attackRow = self.boardRow + dir.dr;
var attackCol = self.boardCol + dir.dc;
if (isValidSquare(attackRow, attackCol)) {
var piece = getPieceAt(attackRow, attackCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: attackRow,
col: attackCol,
isSeaMonsterAttack: true // Sea monster attack, doesn't move
});
}
}
}
// 3. Normal L-shape attacks
var lShapeAttacks = [{
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 < lShapeAttacks.length; i++) {
var move = lShapeAttacks[i];
var attackRow = self.boardRow + move.dr;
var attackCol = self.boardCol + move.dc;
if (isValidSquare(attackRow, attackCol)) {
var piece = getPieceAt(attackRow, attackCol);
if (piece && piece.pieceColor !== self.pieceColor) {
moves.push({
row: attackRow,
col: attackCol,
isSeaMonsterAttack: true // Sea monster attack, doesn't move
});
}
}
}
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
if (self.pieceType === 'king' && !self.hasMoved && !isInCheck(self.pieceColor)) {
// 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 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
};
}
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];
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);
}
} 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 if (moveData && moveData.isSeaMonsterAttack) {
// Sea monster attack - capture piece but don't move
capturedPiece = getPieceAt(targetRow, targetCol);
if (capturedPiece) {
capturedPieces.push(capturedPiece);
}
// Sea monster stays in place, so don't call piece.moveTo()
} 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 the piece if it's not a sea monster attack
if (!(moveData && moveData.isSeaMonsterAttack)) {
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
if (piece.pieceType === 'queen' && !piece.isPromoted) {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
piece.isPromoted = true;
// Visual feedback for promotion
LK.effects.flashObject(piece, 0xFFD700, 1000); // Flash gold
}
}
// Check for minister promotion
if (piece.pieceType === 'minister' && !piece.isPromoted) {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
piece.isPromoted = true;
// Visual feedback for promotion
LK.effects.flashObject(piece, 0x9932CC, 1000); // Flash purple
}
}
// Check for Pawn of the War Machine promotion
if (piece.pieceType === 'pawnOfTheWarMachine') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Camel Pawn promotion
if (piece.pieceType === 'camelPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Minister's Pawn promotion
if (piece.pieceType === 'ministersPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// Transform into promoted minister
piece.pieceType = 'minister';
piece.isPromoted = true;
// 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
}
}
// Check for Pawn of the Elephant promotion
if (piece.pieceType === 'pawnOfTheElephant') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for King's Pawn promotion
if (piece.pieceType === 'kingsPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Vizier's Pawn promotion
if (piece.pieceType === 'viziersPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// Transform into promoted vizier
piece.pieceType = 'queen';
piece.isPromoted = true;
// 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
}
}
// Check for Giraffe Pawn promotion
if (piece.pieceType === 'giraffePawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Knight Pawn promotion
if (piece.pieceType === 'knightPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Rook Pawn promotion
if (piece.pieceType === 'rookPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Pawn of the Leader promotion
if (piece.pieceType === 'pawnOfTheLeader') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Guard's Pawn promotion
if (piece.pieceType === 'guardsPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Pawn of the Bull promotion
if (piece.pieceType === 'pawnOfTheBull') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Lion's Pawn promotion
if (piece.pieceType === 'lionsPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for regular pawn promotion to pawn's pawn
if (piece.pieceType === 'pawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
// Check for Pawn's Pawn promotion
if (piece.pieceType === 'pawnsPawn') {
var opponentLastRow = piece.pieceColor === 0 ? 0 : BOARD_HEIGHT - 1;
if (targetRow === opponentLastRow) {
// 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
}
}
}
// 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 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.setScore(1);
LK.showGameOver();
} else if (!hasValid) {
// Stalemate
LK.setScore(0);
LK.showGameOver();
}
}
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) {
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) {
initializeGame();
}
// 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!)';
}
turnText.setText(playerText);
};
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
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;
}
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) {
// 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;
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
@@ -2027,49 +2027,8 @@
});
}
}
}
- // 4. Long L-shape attacks (3+1)
- var longLShapeAttacks = [{
- 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 < longLShapeAttacks.length; i++) {
- var move = longLShapeAttacks[i];
- var attackRow = self.boardRow + move.dr;
- var attackCol = self.boardCol + move.dc;
- if (isValidSquare(attackRow, attackCol)) {
- var piece = getPieceAt(attackRow, attackCol);
- if (piece && piece.pieceColor !== self.pieceColor) {
- moves.push({
- row: attackRow,
- col: attackCol,
- isSeaMonsterAttack: true // Sea monster attack, doesn't move
- });
- }
- }
- }
break;
}
// Filter out sea squares - pieces cannot enter the sea (except sea monster)
var filteredMoves = [];