User prompt
Taşları hareket ettirdiğimizde tas soundu çalsın.
User prompt
Şahlar birbirlerine 3 kare yatay veya dikey yaklaşamasınlar
User prompt
Oyuna kazanma kaybetme ekle.Satrançta ki kurallardaki gibi kazanma ve kaybetme kuralları olsun.
User prompt
Rakip yapay zekanın önemli bir taşı tehdit altındaysa o önemli taşı kaçırsın
User prompt
Her iki tarafın kendi süresi olsun
User prompt
Süreyi birazdaha sağ kaydır
User prompt
Süreyi biraz sağa kaydır
User prompt
Dakika yazma yerlerini değiştir.
User prompt
Dakikaların yazma yerlerini değiştir benim süremi benim tarafıma onun süresini onun tarafında boş bir yere koy.
User prompt
Her iki tarafında 10 dakika süresi olsun her hamle oynadığında süre bir sonrakinin süresini başlatsın kendi süresi dursun.
User prompt
Taşlara dokunduğunda gitmek istediğin yeri seçerek direk gitmesini sağla. Sürüklemek zorunda kalmasın
User prompt
Her iki tarafında piyonların boyutunu çok az küçült.
User prompt
Her iki tarafın şahlarının boyutunu büyütm
User prompt
Yapay zeka benim hamlelerime bakarak benim gibi oynamaya çalışsın ve yapay zeka hemen oynamak yerine biraz düşünerek oynasın.
User prompt
Rakip yapay zeka daha zeki olsun
User prompt
Taşların oynayabileceği kareleri gösteren kare göstergeyi yuvarlak yap ve tam ortaya koy.
User prompt
Bana taşların görünümlerini asetlere ekle
User prompt
Renkler satranç renklerine benzesin
User prompt
Rakibi yapay zeka oynasın
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'var moves = getLegalMoves(p, x, y, true);' Line Number: 507
Code edit (1 edits merged)
Please save this source code
User prompt
Chess Master: Satranç
Initial prompt
Satranç
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // --- ChessBoard Class --- var ChessBoard = Container.expand(function () { var self = Container.call(this); // 8x8 array of ChessPiece or null self.squares = []; for (var y = 0; y < 8; y++) { self.squares[y] = []; for (var x = 0; x < 8; x++) { self.squares[y][x] = null; } } // Draw board squares self.squareNodes = []; for (var y = 0; y < 8; y++) { self.squareNodes[y] = []; for (var x = 0; x < 8; x++) { var isLight = (x + y) % 2 === 0; var color = isLight ? 0xf0d9b5 : 0xb58863; var assetId = 'board_' + (isLight ? 'light' : 'dark'); var node = LK.getAsset(assetId, { anchorX: 0, anchorY: 0, x: boardOriginX + x * squareSize, y: boardOriginY + y * squareSize }); self.addChild(node); self.squareNodes[y][x] = node; } } // Highlight squares (for moves) self.highlightNodes = []; for (var i = 0; i < 64; i++) { var highlightId = 'highlight_' + i; var node = LK.getAsset(highlightId, { anchorX: 0, anchorY: 0, x: -1000, y: -1000 }); // Hide initially node.alpha = 0.25; self.addChild(node); self.highlightNodes[i] = node; } // Place pieces in starting position self.reset = function () { // Remove all pieces for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { if (self.squares[y][x]) { self.squares[y][x].destroy(); self.squares[y][x] = null; } } } // Place pawns for (var x = 0; x < 8; x++) { var wp = new ChessPiece(); wp.init('pawn', 'white', x, 6); wp.moveTo(x, 6, false); self.addChild(wp); self.squares[6][x] = wp; var bp = new ChessPiece(); bp.init('pawn', 'black', x, 1); bp.moveTo(x, 1, false); self.addChild(bp); self.squares[1][x] = bp; } // Place other pieces var order = ['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook']; for (var x = 0; x < 8; x++) { var w = new ChessPiece(); w.init(order[x], 'white', x, 7); w.moveTo(x, 7, false); self.addChild(w); self.squares[7][x] = w; var b = new ChessPiece(); b.init(order[x], 'black', x, 0); b.moveTo(x, 0, false); self.addChild(b); self.squares[0][x] = b; } }; // Remove all highlights self.clearHighlights = function () { for (var i = 0; i < 64; i++) { self.highlightNodes[i].x = -1000; self.highlightNodes[i].y = -1000; } }; // Highlight given squares (array of {x, y}) self.showHighlights = function (squares) { self.clearHighlights(); for (var i = 0; i < squares.length; i++) { var idx = squares[i].y * 8 + squares[i].x; var node = self.highlightNodes[idx]; node.x = boardOriginX + squares[i].x * squareSize; node.y = boardOriginY + squares[i].y * squareSize; } }; // Get piece at board position self.getPiece = function (x, y) { if (x < 0 || x > 7 || y < 0 || y > 7) return null; return self.squares[y][x]; }; // Set piece at board position self.setPiece = function (x, y, piece) { self.squares[y][x] = piece; }; // Remove piece at board position self.removePiece = function (x, y) { var p = self.squares[y][x]; if (p) { p.destroy(); self.squares[y][x] = null; } }; return self; }); // --- ChessPiece Class --- var ChessPiece = Container.expand(function () { var self = Container.call(this); // Properties self.type = null; // 'pawn', 'rook', 'knight', 'bishop', 'queen', 'king' self.color = null; // 'white' or 'black' self.boardX = 0; // 0-7 self.boardY = 0; // 0-7 self.hasMoved = false; // For castling/en passant self.promoted = false; // For pawn promotion // Asset self.pieceAsset = null; // Set piece type and color, and create asset self.init = function (type, color, boardX, boardY) { self.type = type; self.color = color; self.boardX = boardX; self.boardY = boardY; self.hasMoved = false; self.promoted = false; // Remove old asset if any if (self.pieceAsset) { self.removeChild(self.pieceAsset); } // Use simple shapes for pieces (different color for each type) var colorMap = { 'white': { 'pawn': 0xffffff, 'rook': 0xe0e0e0, 'knight': 0xc0c0c0, 'bishop': 0xa0a0a0, 'queen': 0xccccff, 'king': 0xffe0cc }, 'black': { 'pawn': 0x222222, 'rook': 0x444444, 'knight': 0x666666, 'bishop': 0x888888, 'queen': 0x444488, 'king': 0x886644 } }; var shape = self.type === 'pawn' || self.type === 'bishop' ? 'ellipse' : 'box'; var assetId = self.color + '_' + self.type; var assetColor = colorMap[self.color][self.type]; var size = pieceSize; self.pieceAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add a crown for promoted pawns if (self.promoted && self.type === 'queen') { // Overlay a small yellow ellipse as a "crown" var crown = LK.getAsset('crown_' + self.color, { anchorX: 0.5, anchorY: 0.5, y: -size * 0.3 }); self.addChild(crown); } }; // Move piece to board position (with optional animation) self.moveTo = function (bx, by, animate) { self.boardX = bx; self.boardY = by; var targetX = boardOriginX + bx * squareSize + squareSize / 2; var targetY = boardOriginY + by * squareSize + squareSize / 2; if (animate) { tween(self, { x: targetX, y: targetY }, { duration: 180, easing: tween.cubicOut }); } else { self.x = targetX; self.y = targetY; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // We need tween for piece movement animations // --- Board and Piece Sizes --- var boardSize = Math.min(2048, 2048); // Square board, fit width var squareSize = Math.floor(boardSize / 8); var pieceSize = Math.floor(squareSize * 0.8); var boardOriginX = Math.floor((2048 - squareSize * 8) / 2); var boardOriginY = Math.floor((2732 - squareSize * 8) / 2); // --- Game State --- var board = new ChessBoard(); game.addChild(board); board.reset(); var currentTurn = 'white'; // 'white' or 'black' var selectedPiece = null; var legalMoves = []; // Array of {x, y, special} var lastMove = null; // {from: {x,y}, to: {x,y}, piece, captured, special} var enPassantTarget = null; // {x, y} or null var halfmoveClock = 0; // For 50-move rule var fullmoveNumber = 1; var gameEnded = false; // --- GUI: Turn Display --- var turnText = new Text2('White to move', { size: 90, fill: "#fff" }); turnText.anchor.set(0.5, 0); LK.gui.top.addChild(turnText); // --- GUI: Promotion Popup --- var promotionPopup = null; // --- Helper Functions --- // Convert screen (x, y) to board (bx, by) function screenToBoard(x, y) { var bx = Math.floor((x - boardOriginX) / squareSize); var by = Math.floor((y - boardOriginY) / squareSize); if (bx < 0 || bx > 7 || by < 0 || by > 7) return null; return { x: bx, y: by }; } // Convert board (bx, by) to screen (x, y) (center of square) function boardToScreen(bx, by) { return { x: boardOriginX + bx * squareSize + squareSize / 2, y: boardOriginY + by * squareSize + squareSize / 2 }; } // Is (x, y) on the board? function isOnBoard(x, y) { return x >= 0 && x < 8 && y >= 0 && y < 8; } // Get all legal moves for a piece at (x, y) function getLegalMoves(piece, x, y, ignoreCheck) { var moves = []; if (!piece) return moves; var color = piece.color; var opp = color === 'white' ? 'black' : 'white'; // Directions for sliding pieces var dirs = { 'rook': [[1, 0], [-1, 0], [0, 1], [0, -1]], 'bishop': [[1, 1], [1, -1], [-1, 1], [-1, -1]], 'queen': [[1, 0], [-1, 0], [0, 1], [0, -1], [1, 1], [1, -1], [-1, 1], [-1, -1]] }; // Pawn moves if (piece.type === 'pawn') { var dir = color === 'white' ? -1 : 1; // Forward if (isOnBoard(x, y + dir) && !board.getPiece(x, y + dir)) { moves.push({ x: x, y: y + dir }); // Double move from start if (!piece.hasMoved && isOnBoard(x, y + 2 * dir) && !board.getPiece(x, y + 2 * dir)) { moves.push({ x: x, y: y + 2 * dir, special: 'double' }); } } // Captures for (var dx = -1; dx <= 1; dx += 2) { var tx = x + dx, ty = y + dir; if (isOnBoard(tx, ty)) { var target = board.getPiece(tx, ty); if (target && target.color === opp) { moves.push({ x: tx, y: ty }); } } } // En passant if (enPassantTarget) { if (Math.abs(enPassantTarget.x - x) === 1 && enPassantTarget.y === y + dir) { moves.push({ x: enPassantTarget.x, y: enPassantTarget.y, special: 'enpassant' }); } } } // Knight moves else if (piece.type === 'knight') { var knightDeltas = [[1, 2], [2, 1], [-1, 2], [-2, 1], [1, -2], [2, -1], [-1, -2], [-2, -1]]; for (var i = 0; i < knightDeltas.length; i++) { var dx = knightDeltas[i][0], dy = knightDeltas[i][1]; var tx = x + dx, ty = y + dy; if (isOnBoard(tx, ty)) { var target = board.getPiece(tx, ty); if (!target || target.color === opp) { moves.push({ x: tx, y: ty }); } } } } // King moves (including castling) else if (piece.type === 'king') { for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var tx = x + dx, ty = y + dy; if (isOnBoard(tx, ty)) { var target = board.getPiece(tx, ty); if (!target || target.color === opp) { moves.push({ x: tx, y: ty }); } } } } // Castling if (!piece.hasMoved && !isInCheck(color)) { // Kingside if (canCastle(color, 'kingside')) { moves.push({ x: x + 2, y: y, special: 'castle_kingside' }); } // Queenside if (canCastle(color, 'queenside')) { moves.push({ x: x - 2, y: y, special: 'castle_queenside' }); } } } // Sliding pieces else if (piece.type === 'rook' || piece.type === 'bishop' || piece.type === 'queen') { var directions = dirs[piece.type]; for (var d = 0; d < directions.length; d++) { var dx = directions[d][0], dy = directions[d][1]; var tx = x + dx, ty = y + dy; while (isOnBoard(tx, ty)) { var target = board.getPiece(tx, ty); if (!target) { moves.push({ x: tx, y: ty }); } else { if (target.color === opp) { moves.push({ x: tx, y: ty }); } break; } tx += dx; ty += dy; } } } // Remove moves that leave king in check (unless ignoreCheck) if (!ignoreCheck) { var filtered = []; for (var i = 0; i < moves.length; i++) { var m = moves[i]; if (!wouldLeaveKingInCheck(piece, x, y, m.x, m.y, m.special)) { filtered.push(m); } } return filtered; } return moves; } // Would moving piece at (x, y) to (tx, ty) leave own king in check? function wouldLeaveKingInCheck(piece, x, y, tx, ty, special) { // Simulate move var origPiece = board.getPiece(tx, ty); var origEnPassant = enPassantTarget; var origHasMoved = piece.hasMoved; var origFrom = board.squares[y][x]; var origTo = board.squares[ty][tx]; // Remove captured piece if (special === 'enpassant') { var dir = piece.color === 'white' ? -1 : 1; var capY = ty - dir; var capPiece = board.getPiece(tx, capY); board.squares[capY][tx] = null; } board.squares[y][x] = null; board.squares[ty][tx] = piece; // For castling, move rook as well var rookMoved = null, rookFrom = null, rookTo = null; if (special === 'castle_kingside') { var row = piece.color === 'white' ? 7 : 0; rookFrom = board.getPiece(7, row); rookTo = board.getPiece(5, row); board.squares[row][7] = null; board.squares[row][5] = rookFrom; } if (special === 'castle_queenside') { var row = piece.color === 'white' ? 7 : 0; rookFrom = board.getPiece(0, row); rookTo = board.getPiece(3, row); board.squares[row][0] = null; board.squares[row][3] = rookFrom; } var inCheck = isInCheck(piece.color); // Undo move board.squares[y][x] = origFrom; board.squares[ty][tx] = origTo; if (special === 'enpassant') { var dir = piece.color === 'white' ? -1 : 1; var capY = ty - dir; board.squares[capY][tx] = origPiece; } if (special === 'castle_kingside') { var row = piece.color === 'white' ? 7 : 0; board.squares[row][7] = rookFrom; board.squares[row][5] = null; } if (special === 'castle_queenside') { var row = piece.color === 'white' ? 7 : 0; board.squares[row][0] = rookFrom; board.squares[row][3] = null; } piece.hasMoved = origHasMoved; enPassantTarget = origEnPassant; return inCheck; } // Is color's king in check? function isInCheck(color) { var kingPos = findKing(color); if (!kingPos) return false; var opp = color === 'white' ? 'black' : 'white'; for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { var p = board.getPiece(x, y); if (p && p.color === opp) { var moves = getLegalMoves(p, x, y, true); for (var i = 0; i < moves.length; i++) { if (moves[i].x === kingPos.x && moves[i].y === kingPos.y) { return true; } } } } } return false; } // Find king of color function findKing(color) { for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { var p = board.getPiece(x, y); if (p && p.type === 'king' && p.color === color) { return { x: x, y: y }; } } } return null; } // Can color castle on side ('kingside' or 'queenside')? function canCastle(color, side) { var row = color === 'white' ? 7 : 0; var king = board.getPiece(4, row); if (!king || king.hasMoved) return false; if (side === 'kingside') { var rook = board.getPiece(7, row); if (!rook || rook.type !== 'rook' || rook.hasMoved) return false; // Squares between must be empty if (board.getPiece(5, row) || board.getPiece(6, row)) return false; // Squares king passes through must not be attacked if (isAttacked(5, row, color) || isAttacked(6, row, color)) return false; return true; } if (side === 'queenside') { var rook = board.getPiece(0, row); if (!rook || rook.type !== 'rook' || rook.hasMoved) return false; if (board.getPiece(1, row) || board.getPiece(2, row) || board.getPiece(3, row)) return false; if (isAttacked(2, row, color) || isAttacked(3, row, color)) return false; return true; } return false; } // Is square (x, y) attacked by opponent of color? function isAttacked(x, y, color) { var opp = color === 'white' ? 'black' : 'white'; for (var yy = 0; yy < 8; yy++) { for (var xx = 0; xx < 8; xx++) { var p = board.getPiece(xx, yy); if (p && p.color === opp) { var moves = getLegalMoves(p, xx, yy, true); for (var i = 0; i < moves.length; i++) { if (moves[i].x === x && moves[i].y === y) { return true; } } } } } return false; } // Get all legal moves for color function getAllLegalMoves(color) { var moves = []; for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { var p = board.getPiece(x, y); if (p && p.color === color) { var pieceMoves = getLegalMoves(p, x, y, false); for (var i = 0; i < pieceMoves.length; i++) { moves.push({ from: { x: x, y: y }, to: { x: pieceMoves[i].x, y: pieceMoves[i].y }, special: pieceMoves[i].special }); } } } } return moves; } // Is the game over? Returns {over: true/false, result: 'checkmate'/'stalemate'/'draw', winner: 'white'/'black'/null} function checkGameEnd() { var moves = getAllLegalMoves(currentTurn); if (moves.length === 0) { if (isInCheck(currentTurn)) { return { over: true, result: 'checkmate', winner: currentTurn === 'white' ? 'black' : 'white' }; } else { return { over: true, result: 'stalemate', winner: null }; } } // 50-move rule if (halfmoveClock >= 100) { return { over: true, result: 'draw', winner: null }; } // Insufficient material (only kings) var onlyKings = true; for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { var p = board.getPiece(x, y); if (p && p.type !== 'king') onlyKings = false; } } if (onlyKings) { return { over: true, result: 'draw', winner: null }; } return { over: false }; } // --- Promotion Popup --- function showPromotionPopup(piece, toX, toY, onSelect) { if (promotionPopup) { promotionPopup.destroy(); promotionPopup = null; } promotionPopup = new Container(); var popupW = squareSize * 4; var popupH = squareSize * 1.2; var popupX = boardOriginX + (toX - 1) * squareSize; var popupY = boardOriginY + toY * squareSize - popupH / 2; var bg = LK.getAsset('promotion_bg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); bg.alpha = 0.95; promotionPopup.addChild(bg); var types = ['queen', 'rook', 'bishop', 'knight']; for (var i = 0; i < types.length; i++) { var t = types[i]; var tempPiece = new ChessPiece(); tempPiece.init(t, piece.color, 0, 0); tempPiece.x = squareSize * (i + 0.5); tempPiece.y = popupH / 2; tempPiece.pieceAsset.width = pieceSize * 0.9; tempPiece.pieceAsset.height = pieceSize * 0.9; (function (type) { tempPiece.down = function (x, y, obj) { if (promotionPopup) { onSelect(type); promotionPopup.destroy(); promotionPopup = null; } }; })(t); promotionPopup.addChild(tempPiece); } promotionPopup.x = popupX; promotionPopup.y = popupY; game.addChild(promotionPopup); } // --- Piece Dragging --- var draggingPiece = null; var dragOffsetX = 0, dragOffsetY = 0; var dragLegalMoves = []; // --- Event Handlers --- // Handle touch/mouse down game.down = function (x, y, obj) { if (gameEnded) return; if (promotionPopup) return; // Block input during promotion var pos = screenToBoard(x, y); if (!pos) return; var p = board.getPiece(pos.x, pos.y); if (p && p.color === currentTurn) { selectedPiece = p; draggingPiece = p; dragLegalMoves = getLegalMoves(p, pos.x, pos.y, false); board.showHighlights(dragLegalMoves); // Offset for dragging dragOffsetX = x - p.x; dragOffsetY = y - p.y; // Bring to front p.parent.removeChild(p); game.addChild(p); } }; // Handle touch/mouse move game.move = function (x, y, obj) { if (gameEnded) return; if (promotionPopup) return; if (draggingPiece) { draggingPiece.x = x - dragOffsetX; draggingPiece.y = y - dragOffsetY; } }; // Handle touch/mouse up game.up = function (x, y, obj) { if (gameEnded) return; if (promotionPopup) return; if (!draggingPiece) { board.clearHighlights(); selectedPiece = null; return; } var p = draggingPiece; var fromX = p.boardX, fromY = p.boardY; var pos = screenToBoard(x, y); var moved = false; if (pos) { // Is this a legal move? for (var i = 0; i < dragLegalMoves.length; i++) { var m = dragLegalMoves[i]; if (m.x === pos.x && m.y === pos.y) { // Move piece doMove(p, fromX, fromY, m.x, m.y, m.special); moved = true; break; } } } if (!moved) { // Snap back p.moveTo(fromX, fromY, true); } board.clearHighlights(); selectedPiece = null; draggingPiece = null; dragLegalMoves = []; }; // --- Move Execution --- function doMove(piece, fromX, fromY, toX, toY, special) { var opp = piece.color === 'white' ? 'black' : 'white'; var captured = null; var isPawnMove = piece.type === 'pawn'; var isCapture = false; // Handle en passant if (special === 'enpassant') { var dir = piece.color === 'white' ? -1 : 1; var capY = toY - dir; captured = board.getPiece(toX, capY); board.removePiece(toX, capY); isCapture = true; } // Handle castling if (special === 'castle_kingside') { var row = piece.color === 'white' ? 7 : 0; var rook = board.getPiece(7, row); board.setPiece(5, row, rook); board.setPiece(7, row, null); rook.moveTo(5, row, true); rook.hasMoved = true; } if (special === 'castle_queenside') { var row = piece.color === 'white' ? 7 : 0; var rook = board.getPiece(0, row); board.setPiece(3, row, rook); board.setPiece(0, row, null); rook.moveTo(3, row, true); rook.hasMoved = true; } // Remove captured piece var target = board.getPiece(toX, toY); if (target && target.color !== piece.color) { captured = target; board.removePiece(toX, toY); isCapture = true; } // Move piece board.setPiece(fromX, fromY, null); board.setPiece(toX, toY, piece); piece.moveTo(toX, toY, true); piece.hasMoved = true; // Pawn promotion if (piece.type === 'pawn' && (toY === 0 || toY === 7)) { showPromotionPopup(piece, toX, toY, function (newType) { piece.type = newType; piece.promoted = newType === 'queen'; piece.init(newType, piece.color, toX, toY); endTurn(piece, fromX, fromY, toX, toY, special, captured, isPawnMove, isCapture); }); return; } endTurn(piece, fromX, fromY, toX, toY, special, captured, isPawnMove, isCapture); } function endTurn(piece, fromX, fromY, toX, toY, special, captured, isPawnMove, isCapture) { // Update en passant if (piece.type === 'pawn' && Math.abs(toY - fromY) === 2) { enPassantTarget = { x: toX, y: (fromY + toY) / 2 }; } else { enPassantTarget = null; } // Update halfmove clock if (isPawnMove || isCapture) { halfmoveClock = 0; } else { halfmoveClock++; } // Update fullmove number if (currentTurn === 'black') { fullmoveNumber++; } // Save last move lastMove = { from: { x: fromX, y: fromY }, to: { x: toX, y: toY }, piece: piece, captured: captured, special: special }; // Switch turn currentTurn = currentTurn === 'white' ? 'black' : 'white'; turnText.setText((currentTurn === 'white' ? 'White' : 'Black') + ' to move'); // Check for game end var end = checkGameEnd(); if (end.over) { gameEnded = true; if (end.result === 'checkmate') { LK.effects.flashScreen(0x00ff00, 800); LK.showYouWin(end.winner === 'white' ? 'White wins by checkmate!' : 'Black wins by checkmate!'); } else if (end.result === 'stalemate') { LK.effects.flashScreen(0xffff00, 800); LK.showYouWin('Draw by stalemate'); } else if (end.result === 'draw') { LK.effects.flashScreen(0xffff00, 800); LK.showYouWin('Draw'); } } } // --- Game Update --- game.update = function () { // No per-frame logic needed for chess }; // --- Reset on Game Over --- LK.on('gameover', function () { // Reset everything board.reset(); currentTurn = 'white'; selectedPiece = null; legalMoves = []; lastMove = null; enPassantTarget = null; halfmoveClock = 0; fullmoveNumber = 1; gameEnded = false; turnText.setText('White to move'); if (promotionPopup) { promotionPopup.destroy(); promotionPopup = null; } });
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,872 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// --- ChessBoard Class ---
+var ChessBoard = Container.expand(function () {
+ var self = Container.call(this);
+ // 8x8 array of ChessPiece or null
+ self.squares = [];
+ for (var y = 0; y < 8; y++) {
+ self.squares[y] = [];
+ for (var x = 0; x < 8; x++) {
+ self.squares[y][x] = null;
+ }
+ }
+ // Draw board squares
+ self.squareNodes = [];
+ for (var y = 0; y < 8; y++) {
+ self.squareNodes[y] = [];
+ for (var x = 0; x < 8; x++) {
+ var isLight = (x + y) % 2 === 0;
+ var color = isLight ? 0xf0d9b5 : 0xb58863;
+ var assetId = 'board_' + (isLight ? 'light' : 'dark');
+ var node = LK.getAsset(assetId, {
+ anchorX: 0,
+ anchorY: 0,
+ x: boardOriginX + x * squareSize,
+ y: boardOriginY + y * squareSize
+ });
+ self.addChild(node);
+ self.squareNodes[y][x] = node;
+ }
+ }
+ // Highlight squares (for moves)
+ self.highlightNodes = [];
+ for (var i = 0; i < 64; i++) {
+ var highlightId = 'highlight_' + i;
+ var node = LK.getAsset(highlightId, {
+ anchorX: 0,
+ anchorY: 0,
+ x: -1000,
+ y: -1000
+ }); // Hide initially
+ node.alpha = 0.25;
+ self.addChild(node);
+ self.highlightNodes[i] = node;
+ }
+ // Place pieces in starting position
+ self.reset = function () {
+ // Remove all pieces
+ for (var y = 0; y < 8; y++) {
+ for (var x = 0; x < 8; x++) {
+ if (self.squares[y][x]) {
+ self.squares[y][x].destroy();
+ self.squares[y][x] = null;
+ }
+ }
+ }
+ // Place pawns
+ for (var x = 0; x < 8; x++) {
+ var wp = new ChessPiece();
+ wp.init('pawn', 'white', x, 6);
+ wp.moveTo(x, 6, false);
+ self.addChild(wp);
+ self.squares[6][x] = wp;
+ var bp = new ChessPiece();
+ bp.init('pawn', 'black', x, 1);
+ bp.moveTo(x, 1, false);
+ self.addChild(bp);
+ self.squares[1][x] = bp;
+ }
+ // Place other pieces
+ var order = ['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook'];
+ for (var x = 0; x < 8; x++) {
+ var w = new ChessPiece();
+ w.init(order[x], 'white', x, 7);
+ w.moveTo(x, 7, false);
+ self.addChild(w);
+ self.squares[7][x] = w;
+ var b = new ChessPiece();
+ b.init(order[x], 'black', x, 0);
+ b.moveTo(x, 0, false);
+ self.addChild(b);
+ self.squares[0][x] = b;
+ }
+ };
+ // Remove all highlights
+ self.clearHighlights = function () {
+ for (var i = 0; i < 64; i++) {
+ self.highlightNodes[i].x = -1000;
+ self.highlightNodes[i].y = -1000;
+ }
+ };
+ // Highlight given squares (array of {x, y})
+ self.showHighlights = function (squares) {
+ self.clearHighlights();
+ for (var i = 0; i < squares.length; i++) {
+ var idx = squares[i].y * 8 + squares[i].x;
+ var node = self.highlightNodes[idx];
+ node.x = boardOriginX + squares[i].x * squareSize;
+ node.y = boardOriginY + squares[i].y * squareSize;
+ }
+ };
+ // Get piece at board position
+ self.getPiece = function (x, y) {
+ if (x < 0 || x > 7 || y < 0 || y > 7) return null;
+ return self.squares[y][x];
+ };
+ // Set piece at board position
+ self.setPiece = function (x, y, piece) {
+ self.squares[y][x] = piece;
+ };
+ // Remove piece at board position
+ self.removePiece = function (x, y) {
+ var p = self.squares[y][x];
+ if (p) {
+ p.destroy();
+ self.squares[y][x] = null;
+ }
+ };
+ return self;
+});
+// --- ChessPiece Class ---
+var ChessPiece = Container.expand(function () {
+ var self = Container.call(this);
+ // Properties
+ self.type = null; // 'pawn', 'rook', 'knight', 'bishop', 'queen', 'king'
+ self.color = null; // 'white' or 'black'
+ self.boardX = 0; // 0-7
+ self.boardY = 0; // 0-7
+ self.hasMoved = false; // For castling/en passant
+ self.promoted = false; // For pawn promotion
+ // Asset
+ self.pieceAsset = null;
+ // Set piece type and color, and create asset
+ self.init = function (type, color, boardX, boardY) {
+ self.type = type;
+ self.color = color;
+ self.boardX = boardX;
+ self.boardY = boardY;
+ self.hasMoved = false;
+ self.promoted = false;
+ // Remove old asset if any
+ if (self.pieceAsset) {
+ self.removeChild(self.pieceAsset);
+ }
+ // Use simple shapes for pieces (different color for each type)
+ var colorMap = {
+ 'white': {
+ 'pawn': 0xffffff,
+ 'rook': 0xe0e0e0,
+ 'knight': 0xc0c0c0,
+ 'bishop': 0xa0a0a0,
+ 'queen': 0xccccff,
+ 'king': 0xffe0cc
+ },
+ 'black': {
+ 'pawn': 0x222222,
+ 'rook': 0x444444,
+ 'knight': 0x666666,
+ 'bishop': 0x888888,
+ 'queen': 0x444488,
+ 'king': 0x886644
+ }
+ };
+ var shape = self.type === 'pawn' || self.type === 'bishop' ? 'ellipse' : 'box';
+ var assetId = self.color + '_' + self.type;
+ var assetColor = colorMap[self.color][self.type];
+ var size = pieceSize;
+ self.pieceAsset = self.attachAsset(assetId, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Add a crown for promoted pawns
+ if (self.promoted && self.type === 'queen') {
+ // Overlay a small yellow ellipse as a "crown"
+ var crown = LK.getAsset('crown_' + self.color, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: -size * 0.3
+ });
+ self.addChild(crown);
+ }
+ };
+ // Move piece to board position (with optional animation)
+ self.moveTo = function (bx, by, animate) {
+ self.boardX = bx;
+ self.boardY = by;
+ var targetX = boardOriginX + bx * squareSize + squareSize / 2;
+ var targetY = boardOriginY + by * squareSize + squareSize / 2;
+ if (animate) {
+ tween(self, {
+ x: targetX,
+ y: targetY
+ }, {
+ duration: 180,
+ easing: tween.cubicOut
+ });
+ } else {
+ self.x = targetX;
+ self.y = targetY;
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
+ backgroundColor: 0x222222
+});
+
+/****
+* Game Code
+****/
+// We need tween for piece movement animations
+// --- Board and Piece Sizes ---
+var boardSize = Math.min(2048, 2048); // Square board, fit width
+var squareSize = Math.floor(boardSize / 8);
+var pieceSize = Math.floor(squareSize * 0.8);
+var boardOriginX = Math.floor((2048 - squareSize * 8) / 2);
+var boardOriginY = Math.floor((2732 - squareSize * 8) / 2);
+// --- Game State ---
+var board = new ChessBoard();
+game.addChild(board);
+board.reset();
+var currentTurn = 'white'; // 'white' or 'black'
+var selectedPiece = null;
+var legalMoves = []; // Array of {x, y, special}
+var lastMove = null; // {from: {x,y}, to: {x,y}, piece, captured, special}
+var enPassantTarget = null; // {x, y} or null
+var halfmoveClock = 0; // For 50-move rule
+var fullmoveNumber = 1;
+var gameEnded = false;
+// --- GUI: Turn Display ---
+var turnText = new Text2('White to move', {
+ size: 90,
+ fill: "#fff"
+});
+turnText.anchor.set(0.5, 0);
+LK.gui.top.addChild(turnText);
+// --- GUI: Promotion Popup ---
+var promotionPopup = null;
+// --- Helper Functions ---
+// Convert screen (x, y) to board (bx, by)
+function screenToBoard(x, y) {
+ var bx = Math.floor((x - boardOriginX) / squareSize);
+ var by = Math.floor((y - boardOriginY) / squareSize);
+ if (bx < 0 || bx > 7 || by < 0 || by > 7) return null;
+ return {
+ x: bx,
+ y: by
+ };
+}
+// Convert board (bx, by) to screen (x, y) (center of square)
+function boardToScreen(bx, by) {
+ return {
+ x: boardOriginX + bx * squareSize + squareSize / 2,
+ y: boardOriginY + by * squareSize + squareSize / 2
+ };
+}
+// Is (x, y) on the board?
+function isOnBoard(x, y) {
+ return x >= 0 && x < 8 && y >= 0 && y < 8;
+}
+// Get all legal moves for a piece at (x, y)
+function getLegalMoves(piece, x, y, ignoreCheck) {
+ var moves = [];
+ if (!piece) return moves;
+ var color = piece.color;
+ var opp = color === 'white' ? 'black' : 'white';
+ // Directions for sliding pieces
+ var dirs = {
+ 'rook': [[1, 0], [-1, 0], [0, 1], [0, -1]],
+ 'bishop': [[1, 1], [1, -1], [-1, 1], [-1, -1]],
+ 'queen': [[1, 0], [-1, 0], [0, 1], [0, -1], [1, 1], [1, -1], [-1, 1], [-1, -1]]
+ };
+ // Pawn moves
+ if (piece.type === 'pawn') {
+ var dir = color === 'white' ? -1 : 1;
+ // Forward
+ if (isOnBoard(x, y + dir) && !board.getPiece(x, y + dir)) {
+ moves.push({
+ x: x,
+ y: y + dir
+ });
+ // Double move from start
+ if (!piece.hasMoved && isOnBoard(x, y + 2 * dir) && !board.getPiece(x, y + 2 * dir)) {
+ moves.push({
+ x: x,
+ y: y + 2 * dir,
+ special: 'double'
+ });
+ }
+ }
+ // Captures
+ for (var dx = -1; dx <= 1; dx += 2) {
+ var tx = x + dx,
+ ty = y + dir;
+ if (isOnBoard(tx, ty)) {
+ var target = board.getPiece(tx, ty);
+ if (target && target.color === opp) {
+ moves.push({
+ x: tx,
+ y: ty
+ });
+ }
+ }
+ }
+ // En passant
+ if (enPassantTarget) {
+ if (Math.abs(enPassantTarget.x - x) === 1 && enPassantTarget.y === y + dir) {
+ moves.push({
+ x: enPassantTarget.x,
+ y: enPassantTarget.y,
+ special: 'enpassant'
+ });
+ }
+ }
+ }
+ // Knight moves
+ else if (piece.type === 'knight') {
+ var knightDeltas = [[1, 2], [2, 1], [-1, 2], [-2, 1], [1, -2], [2, -1], [-1, -2], [-2, -1]];
+ for (var i = 0; i < knightDeltas.length; i++) {
+ var dx = knightDeltas[i][0],
+ dy = knightDeltas[i][1];
+ var tx = x + dx,
+ ty = y + dy;
+ if (isOnBoard(tx, ty)) {
+ var target = board.getPiece(tx, ty);
+ if (!target || target.color === opp) {
+ moves.push({
+ x: tx,
+ y: ty
+ });
+ }
+ }
+ }
+ }
+ // King moves (including castling)
+ else if (piece.type === 'king') {
+ for (var dx = -1; dx <= 1; dx++) {
+ for (var dy = -1; dy <= 1; dy++) {
+ if (dx === 0 && dy === 0) continue;
+ var tx = x + dx,
+ ty = y + dy;
+ if (isOnBoard(tx, ty)) {
+ var target = board.getPiece(tx, ty);
+ if (!target || target.color === opp) {
+ moves.push({
+ x: tx,
+ y: ty
+ });
+ }
+ }
+ }
+ }
+ // Castling
+ if (!piece.hasMoved && !isInCheck(color)) {
+ // Kingside
+ if (canCastle(color, 'kingside')) {
+ moves.push({
+ x: x + 2,
+ y: y,
+ special: 'castle_kingside'
+ });
+ }
+ // Queenside
+ if (canCastle(color, 'queenside')) {
+ moves.push({
+ x: x - 2,
+ y: y,
+ special: 'castle_queenside'
+ });
+ }
+ }
+ }
+ // Sliding pieces
+ else if (piece.type === 'rook' || piece.type === 'bishop' || piece.type === 'queen') {
+ var directions = dirs[piece.type];
+ for (var d = 0; d < directions.length; d++) {
+ var dx = directions[d][0],
+ dy = directions[d][1];
+ var tx = x + dx,
+ ty = y + dy;
+ while (isOnBoard(tx, ty)) {
+ var target = board.getPiece(tx, ty);
+ if (!target) {
+ moves.push({
+ x: tx,
+ y: ty
+ });
+ } else {
+ if (target.color === opp) {
+ moves.push({
+ x: tx,
+ y: ty
+ });
+ }
+ break;
+ }
+ tx += dx;
+ ty += dy;
+ }
+ }
+ }
+ // Remove moves that leave king in check (unless ignoreCheck)
+ if (!ignoreCheck) {
+ var filtered = [];
+ for (var i = 0; i < moves.length; i++) {
+ var m = moves[i];
+ if (!wouldLeaveKingInCheck(piece, x, y, m.x, m.y, m.special)) {
+ filtered.push(m);
+ }
+ }
+ return filtered;
+ }
+ return moves;
+}
+// Would moving piece at (x, y) to (tx, ty) leave own king in check?
+function wouldLeaveKingInCheck(piece, x, y, tx, ty, special) {
+ // Simulate move
+ var origPiece = board.getPiece(tx, ty);
+ var origEnPassant = enPassantTarget;
+ var origHasMoved = piece.hasMoved;
+ var origFrom = board.squares[y][x];
+ var origTo = board.squares[ty][tx];
+ // Remove captured piece
+ if (special === 'enpassant') {
+ var dir = piece.color === 'white' ? -1 : 1;
+ var capY = ty - dir;
+ var capPiece = board.getPiece(tx, capY);
+ board.squares[capY][tx] = null;
+ }
+ board.squares[y][x] = null;
+ board.squares[ty][tx] = piece;
+ // For castling, move rook as well
+ var rookMoved = null,
+ rookFrom = null,
+ rookTo = null;
+ if (special === 'castle_kingside') {
+ var row = piece.color === 'white' ? 7 : 0;
+ rookFrom = board.getPiece(7, row);
+ rookTo = board.getPiece(5, row);
+ board.squares[row][7] = null;
+ board.squares[row][5] = rookFrom;
+ }
+ if (special === 'castle_queenside') {
+ var row = piece.color === 'white' ? 7 : 0;
+ rookFrom = board.getPiece(0, row);
+ rookTo = board.getPiece(3, row);
+ board.squares[row][0] = null;
+ board.squares[row][3] = rookFrom;
+ }
+ var inCheck = isInCheck(piece.color);
+ // Undo move
+ board.squares[y][x] = origFrom;
+ board.squares[ty][tx] = origTo;
+ if (special === 'enpassant') {
+ var dir = piece.color === 'white' ? -1 : 1;
+ var capY = ty - dir;
+ board.squares[capY][tx] = origPiece;
+ }
+ if (special === 'castle_kingside') {
+ var row = piece.color === 'white' ? 7 : 0;
+ board.squares[row][7] = rookFrom;
+ board.squares[row][5] = null;
+ }
+ if (special === 'castle_queenside') {
+ var row = piece.color === 'white' ? 7 : 0;
+ board.squares[row][0] = rookFrom;
+ board.squares[row][3] = null;
+ }
+ piece.hasMoved = origHasMoved;
+ enPassantTarget = origEnPassant;
+ return inCheck;
+}
+// Is color's king in check?
+function isInCheck(color) {
+ var kingPos = findKing(color);
+ if (!kingPos) return false;
+ var opp = color === 'white' ? 'black' : 'white';
+ for (var y = 0; y < 8; y++) {
+ for (var x = 0; x < 8; x++) {
+ var p = board.getPiece(x, y);
+ if (p && p.color === opp) {
+ var moves = getLegalMoves(p, x, y, true);
+ for (var i = 0; i < moves.length; i++) {
+ if (moves[i].x === kingPos.x && moves[i].y === kingPos.y) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+// Find king of color
+function findKing(color) {
+ for (var y = 0; y < 8; y++) {
+ for (var x = 0; x < 8; x++) {
+ var p = board.getPiece(x, y);
+ if (p && p.type === 'king' && p.color === color) {
+ return {
+ x: x,
+ y: y
+ };
+ }
+ }
+ }
+ return null;
+}
+// Can color castle on side ('kingside' or 'queenside')?
+function canCastle(color, side) {
+ var row = color === 'white' ? 7 : 0;
+ var king = board.getPiece(4, row);
+ if (!king || king.hasMoved) return false;
+ if (side === 'kingside') {
+ var rook = board.getPiece(7, row);
+ if (!rook || rook.type !== 'rook' || rook.hasMoved) return false;
+ // Squares between must be empty
+ if (board.getPiece(5, row) || board.getPiece(6, row)) return false;
+ // Squares king passes through must not be attacked
+ if (isAttacked(5, row, color) || isAttacked(6, row, color)) return false;
+ return true;
+ }
+ if (side === 'queenside') {
+ var rook = board.getPiece(0, row);
+ if (!rook || rook.type !== 'rook' || rook.hasMoved) return false;
+ if (board.getPiece(1, row) || board.getPiece(2, row) || board.getPiece(3, row)) return false;
+ if (isAttacked(2, row, color) || isAttacked(3, row, color)) return false;
+ return true;
+ }
+ return false;
+}
+// Is square (x, y) attacked by opponent of color?
+function isAttacked(x, y, color) {
+ var opp = color === 'white' ? 'black' : 'white';
+ for (var yy = 0; yy < 8; yy++) {
+ for (var xx = 0; xx < 8; xx++) {
+ var p = board.getPiece(xx, yy);
+ if (p && p.color === opp) {
+ var moves = getLegalMoves(p, xx, yy, true);
+ for (var i = 0; i < moves.length; i++) {
+ if (moves[i].x === x && moves[i].y === y) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+// Get all legal moves for color
+function getAllLegalMoves(color) {
+ var moves = [];
+ for (var y = 0; y < 8; y++) {
+ for (var x = 0; x < 8; x++) {
+ var p = board.getPiece(x, y);
+ if (p && p.color === color) {
+ var pieceMoves = getLegalMoves(p, x, y, false);
+ for (var i = 0; i < pieceMoves.length; i++) {
+ moves.push({
+ from: {
+ x: x,
+ y: y
+ },
+ to: {
+ x: pieceMoves[i].x,
+ y: pieceMoves[i].y
+ },
+ special: pieceMoves[i].special
+ });
+ }
+ }
+ }
+ }
+ return moves;
+}
+// Is the game over? Returns {over: true/false, result: 'checkmate'/'stalemate'/'draw', winner: 'white'/'black'/null}
+function checkGameEnd() {
+ var moves = getAllLegalMoves(currentTurn);
+ if (moves.length === 0) {
+ if (isInCheck(currentTurn)) {
+ return {
+ over: true,
+ result: 'checkmate',
+ winner: currentTurn === 'white' ? 'black' : 'white'
+ };
+ } else {
+ return {
+ over: true,
+ result: 'stalemate',
+ winner: null
+ };
+ }
+ }
+ // 50-move rule
+ if (halfmoveClock >= 100) {
+ return {
+ over: true,
+ result: 'draw',
+ winner: null
+ };
+ }
+ // Insufficient material (only kings)
+ var onlyKings = true;
+ for (var y = 0; y < 8; y++) {
+ for (var x = 0; x < 8; x++) {
+ var p = board.getPiece(x, y);
+ if (p && p.type !== 'king') onlyKings = false;
+ }
+ }
+ if (onlyKings) {
+ return {
+ over: true,
+ result: 'draw',
+ winner: null
+ };
+ }
+ return {
+ over: false
+ };
+}
+// --- Promotion Popup ---
+function showPromotionPopup(piece, toX, toY, onSelect) {
+ if (promotionPopup) {
+ promotionPopup.destroy();
+ promotionPopup = null;
+ }
+ promotionPopup = new Container();
+ var popupW = squareSize * 4;
+ var popupH = squareSize * 1.2;
+ var popupX = boardOriginX + (toX - 1) * squareSize;
+ var popupY = boardOriginY + toY * squareSize - popupH / 2;
+ var bg = LK.getAsset('promotion_bg', {
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0
+ });
+ bg.alpha = 0.95;
+ promotionPopup.addChild(bg);
+ var types = ['queen', 'rook', 'bishop', 'knight'];
+ for (var i = 0; i < types.length; i++) {
+ var t = types[i];
+ var tempPiece = new ChessPiece();
+ tempPiece.init(t, piece.color, 0, 0);
+ tempPiece.x = squareSize * (i + 0.5);
+ tempPiece.y = popupH / 2;
+ tempPiece.pieceAsset.width = pieceSize * 0.9;
+ tempPiece.pieceAsset.height = pieceSize * 0.9;
+ (function (type) {
+ tempPiece.down = function (x, y, obj) {
+ if (promotionPopup) {
+ onSelect(type);
+ promotionPopup.destroy();
+ promotionPopup = null;
+ }
+ };
+ })(t);
+ promotionPopup.addChild(tempPiece);
+ }
+ promotionPopup.x = popupX;
+ promotionPopup.y = popupY;
+ game.addChild(promotionPopup);
+}
+// --- Piece Dragging ---
+var draggingPiece = null;
+var dragOffsetX = 0,
+ dragOffsetY = 0;
+var dragLegalMoves = [];
+// --- Event Handlers ---
+// Handle touch/mouse down
+game.down = function (x, y, obj) {
+ if (gameEnded) return;
+ if (promotionPopup) return; // Block input during promotion
+ var pos = screenToBoard(x, y);
+ if (!pos) return;
+ var p = board.getPiece(pos.x, pos.y);
+ if (p && p.color === currentTurn) {
+ selectedPiece = p;
+ draggingPiece = p;
+ dragLegalMoves = getLegalMoves(p, pos.x, pos.y, false);
+ board.showHighlights(dragLegalMoves);
+ // Offset for dragging
+ dragOffsetX = x - p.x;
+ dragOffsetY = y - p.y;
+ // Bring to front
+ p.parent.removeChild(p);
+ game.addChild(p);
+ }
+};
+// Handle touch/mouse move
+game.move = function (x, y, obj) {
+ if (gameEnded) return;
+ if (promotionPopup) return;
+ if (draggingPiece) {
+ draggingPiece.x = x - dragOffsetX;
+ draggingPiece.y = y - dragOffsetY;
+ }
+};
+// Handle touch/mouse up
+game.up = function (x, y, obj) {
+ if (gameEnded) return;
+ if (promotionPopup) return;
+ if (!draggingPiece) {
+ board.clearHighlights();
+ selectedPiece = null;
+ return;
+ }
+ var p = draggingPiece;
+ var fromX = p.boardX,
+ fromY = p.boardY;
+ var pos = screenToBoard(x, y);
+ var moved = false;
+ if (pos) {
+ // Is this a legal move?
+ for (var i = 0; i < dragLegalMoves.length; i++) {
+ var m = dragLegalMoves[i];
+ if (m.x === pos.x && m.y === pos.y) {
+ // Move piece
+ doMove(p, fromX, fromY, m.x, m.y, m.special);
+ moved = true;
+ break;
+ }
+ }
+ }
+ if (!moved) {
+ // Snap back
+ p.moveTo(fromX, fromY, true);
+ }
+ board.clearHighlights();
+ selectedPiece = null;
+ draggingPiece = null;
+ dragLegalMoves = [];
+};
+// --- Move Execution ---
+function doMove(piece, fromX, fromY, toX, toY, special) {
+ var opp = piece.color === 'white' ? 'black' : 'white';
+ var captured = null;
+ var isPawnMove = piece.type === 'pawn';
+ var isCapture = false;
+ // Handle en passant
+ if (special === 'enpassant') {
+ var dir = piece.color === 'white' ? -1 : 1;
+ var capY = toY - dir;
+ captured = board.getPiece(toX, capY);
+ board.removePiece(toX, capY);
+ isCapture = true;
+ }
+ // Handle castling
+ if (special === 'castle_kingside') {
+ var row = piece.color === 'white' ? 7 : 0;
+ var rook = board.getPiece(7, row);
+ board.setPiece(5, row, rook);
+ board.setPiece(7, row, null);
+ rook.moveTo(5, row, true);
+ rook.hasMoved = true;
+ }
+ if (special === 'castle_queenside') {
+ var row = piece.color === 'white' ? 7 : 0;
+ var rook = board.getPiece(0, row);
+ board.setPiece(3, row, rook);
+ board.setPiece(0, row, null);
+ rook.moveTo(3, row, true);
+ rook.hasMoved = true;
+ }
+ // Remove captured piece
+ var target = board.getPiece(toX, toY);
+ if (target && target.color !== piece.color) {
+ captured = target;
+ board.removePiece(toX, toY);
+ isCapture = true;
+ }
+ // Move piece
+ board.setPiece(fromX, fromY, null);
+ board.setPiece(toX, toY, piece);
+ piece.moveTo(toX, toY, true);
+ piece.hasMoved = true;
+ // Pawn promotion
+ if (piece.type === 'pawn' && (toY === 0 || toY === 7)) {
+ showPromotionPopup(piece, toX, toY, function (newType) {
+ piece.type = newType;
+ piece.promoted = newType === 'queen';
+ piece.init(newType, piece.color, toX, toY);
+ endTurn(piece, fromX, fromY, toX, toY, special, captured, isPawnMove, isCapture);
+ });
+ return;
+ }
+ endTurn(piece, fromX, fromY, toX, toY, special, captured, isPawnMove, isCapture);
+}
+function endTurn(piece, fromX, fromY, toX, toY, special, captured, isPawnMove, isCapture) {
+ // Update en passant
+ if (piece.type === 'pawn' && Math.abs(toY - fromY) === 2) {
+ enPassantTarget = {
+ x: toX,
+ y: (fromY + toY) / 2
+ };
+ } else {
+ enPassantTarget = null;
+ }
+ // Update halfmove clock
+ if (isPawnMove || isCapture) {
+ halfmoveClock = 0;
+ } else {
+ halfmoveClock++;
+ }
+ // Update fullmove number
+ if (currentTurn === 'black') {
+ fullmoveNumber++;
+ }
+ // Save last move
+ lastMove = {
+ from: {
+ x: fromX,
+ y: fromY
+ },
+ to: {
+ x: toX,
+ y: toY
+ },
+ piece: piece,
+ captured: captured,
+ special: special
+ };
+ // Switch turn
+ currentTurn = currentTurn === 'white' ? 'black' : 'white';
+ turnText.setText((currentTurn === 'white' ? 'White' : 'Black') + ' to move');
+ // Check for game end
+ var end = checkGameEnd();
+ if (end.over) {
+ gameEnded = true;
+ if (end.result === 'checkmate') {
+ LK.effects.flashScreen(0x00ff00, 800);
+ LK.showYouWin(end.winner === 'white' ? 'White wins by checkmate!' : 'Black wins by checkmate!');
+ } else if (end.result === 'stalemate') {
+ LK.effects.flashScreen(0xffff00, 800);
+ LK.showYouWin('Draw by stalemate');
+ } else if (end.result === 'draw') {
+ LK.effects.flashScreen(0xffff00, 800);
+ LK.showYouWin('Draw');
+ }
+ }
+}
+// --- Game Update ---
+game.update = function () {
+ // No per-frame logic needed for chess
+};
+// --- Reset on Game Over ---
+LK.on('gameover', function () {
+ // Reset everything
+ board.reset();
+ currentTurn = 'white';
+ selectedPiece = null;
+ legalMoves = [];
+ lastMove = null;
+ enPassantTarget = null;
+ halfmoveClock = 0;
+ fullmoveNumber = 1;
+ gameEnded = false;
+ turnText.setText('White to move');
+ if (promotionPopup) {
+ promotionPopup.destroy();
+ promotionPopup = null;
+ }
});
\ No newline at end of file