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; // Use classic chessboard colors: light = #f0d9b5, dark = #b58863 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, width: squareSize, height: squareSize, color: color, shape: 'box' }); 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; // Use a circle (ellipse) and center it in the square var node = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: Math.floor(squareSize * 0.45), height: Math.floor(squareSize * 0.45), 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]; // Center the highlight circle in the square node.x = boardOriginX + squares[i].x * squareSize + squareSize / 2; node.y = boardOriginY + squares[i].y * squareSize + squareSize / 2; } }; // 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 classic piece colors: white = #fff, black = #222 var colorMap = { 'white': { 'pawn': 0xffffff, 'rook': 0xffffff, 'knight': 0xffffff, 'bishop': 0xffffff, 'queen': 0xffffff, 'king': 0xffffff }, 'black': { 'pawn': 0x222222, 'rook': 0x222222, 'knight': 0x222222, 'bishop': 0x222222, 'queen': 0x222222, 'king': 0x222222 } }; 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 = self.type === 'king' ? Math.floor(pieceSize * 1.25) : pieceSize; self.pieceAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, width: size, height: size, color: assetColor, shape: shape }); // 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 ****/ // --- Board and Piece Sizes --- // We need tween for piece movement animations // Board squares // White pieces // Black pieces // Crown for promoted pawn (queen) 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: Timers --- // Each player starts with 10 minutes (600 seconds) var whiteTime = 600; var blackTime = 600; var whiteTimerText = new Text2('10:00', { size: 90, fill: "#fff" }); // Place white timer near bottom left, above white's side, but not in the bottom left 100x100 reserved area whiteTimerText.anchor.set(0, 1); // left aligned, bottom whiteTimerText.x = 320; // moved even further right whiteTimerText.y = LK.gui.height - 40; LK.gui.bottomLeft.addChild(whiteTimerText); var blackTimerText = new Text2('10:00', { size: 90, fill: "#fff" }); // Place black timer near top right, above black's side, but not in the top right 100x100 reserved area blackTimerText.anchor.set(1, 0); // right aligned, top blackTimerText.x = LK.gui.width - 40; // moved even further right (inward) blackTimerText.y = 0; LK.gui.topRight.addChild(blackTimerText); // Timer state var whiteTimerInterval = null; var blackTimerInterval = null; // Helper to format seconds as MM:SS function formatTime(secs) { var m = Math.floor(secs / 60); var s = Math.floor(secs % 60); return (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; } // Start/stop timer helpers function startWhiteTimer() { if (whiteTimerInterval) LK.clearInterval(whiteTimerInterval); whiteTimerInterval = LK.setInterval(function () { if (gameEnded) return; whiteTime--; if (whiteTime < 0) whiteTime = 0; whiteTimerText.setText(formatTime(whiteTime)); if (whiteTime === 0) { gameEnded = true; LK.effects.flashScreen(0xff0000, 800); LK.showYouWin('Black wins on time!'); stopAllTimers(); } }, 1000); } function startBlackTimer() { if (blackTimerInterval) LK.clearInterval(blackTimerInterval); blackTimerInterval = LK.setInterval(function () { if (gameEnded) return; blackTime--; if (blackTime < 0) blackTime = 0; blackTimerText.setText(formatTime(blackTime)); if (blackTime === 0) { gameEnded = true; LK.effects.flashScreen(0xff0000, 800); LK.showYouWin('White wins on time!'); stopAllTimers(); } }, 1000); } function stopWhiteTimer() { if (whiteTimerInterval) { LK.clearInterval(whiteTimerInterval); whiteTimerInterval = null; } } function stopBlackTimer() { if (blackTimerInterval) { LK.clearInterval(blackTimerInterval); blackTimerInterval = null; } } function stopAllTimers() { stopWhiteTimer(); stopBlackTimer(); } // Start white's timer at game start startWhiteTimer(); whiteTimerText.setText(formatTime(whiteTime)); blackTimerText.setText(formatTime(blackTime)); // --- 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') { // Prevent infinite recursion: when ignoreCheck is true, do NOT generate king moves if (ignoreCheck) { // Do not generate king moves when ignoreCheck is true (used for attack detection) // This prevents infinite recursion between isInCheck and getLegalMoves } else { // Find the other king's position var otherKingPos = null; for (var yy = 0; yy < 8; yy++) { for (var xx = 0; xx < 8; xx++) { var p = board.getPiece(xx, yy); if (p && p.type === 'king' && p.color !== color) { otherKingPos = { x: xx, y: yy }; break; } } if (otherKingPos) break; } 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)) { // Prevent moving within 3 squares horizontally or vertically of the other king var allow = true; if (otherKingPos) { if (Math.abs(tx - otherKingPos.x) <= 3 && ty === otherKingPos.y) allow = false; if (Math.abs(ty - otherKingPos.y) <= 3 && tx === otherKingPos.x) allow = false; if (tx === otherKingPos.x && ty === otherKingPos.y) allow = false; } var target = board.getPiece(tx, ty); if ((!target || target.color === opp) && allow) { moves.push({ x: tx, y: ty }); } } } } // Castling if (!piece.hasMoved && !isInCheck(color)) { // Kingside if (canCastle(color, 'kingside')) { // Check that castling does not violate the king distance rule var castleTx = x + 2; var castleTy = y; var allowCastle = true; if (otherKingPos) { if (Math.abs(castleTx - otherKingPos.x) <= 3 && castleTy === otherKingPos.y) allowCastle = false; if (Math.abs(castleTy - otherKingPos.y) <= 3 && castleTx === otherKingPos.x) allowCastle = false; if (castleTx === otherKingPos.x && castleTy === otherKingPos.y) allowCastle = false; } if (allowCastle) { moves.push({ x: castleTx, y: castleTy, special: 'castle_kingside' }); } } // Queenside if (canCastle(color, 'queenside')) { var castleTx = x - 2; var castleTy = y; var allowCastle = true; if (otherKingPos) { if (Math.abs(castleTx - otherKingPos.x) <= 3 && castleTy === otherKingPos.y) allowCastle = false; if (Math.abs(castleTy - otherKingPos.y) <= 3 && castleTx === otherKingPos.x) allowCastle = false; if (castleTx === otherKingPos.x && castleTy === otherKingPos.y) allowCastle = false; } if (allowCastle) { moves.push({ x: castleTx, y: castleTy, 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); } // --- Tap-to-select and tap-to-move --- // No dragging variables needed // --- Event Handlers --- // Handle touch/mouse down (tap) 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 a piece is already selected and the tap is on a legal move, move there if (selectedPiece && legalMoves.length > 0) { for (var i = 0; i < legalMoves.length; i++) { var m = legalMoves[i]; if (m.x === pos.x && m.y === pos.y) { // Move piece doMove(selectedPiece, selectedPiece.boardX, selectedPiece.boardY, m.x, m.y, m.special); board.clearHighlights(); selectedPiece = null; legalMoves = []; return; } } } // If tap is on a piece of current turn, select it and show moves if (p && p.color === currentTurn) { selectedPiece = p; legalMoves = getLegalMoves(p, pos.x, pos.y, false); board.showHighlights(legalMoves); return; } // If tap is on empty square or not a legal move, clear selection board.clearHighlights(); selectedPiece = null; legalMoves = []; }; // No dragging, so move and up handlers are not needed game.move = function (x, y, obj) {}; game.up = function (x, y, obj) {}; // --- 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; // Play move sound LK.getSound('tas').play(); // 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 if (currentTurn === 'white') { stopWhiteTimer(); startBlackTimer(); currentTurn = 'black'; } else { stopBlackTimer(); startWhiteTimer(); currentTurn = 'white'; } turnText.setText((currentTurn === 'white' ? 'White' : 'Black') + ' to move'); // Check for game end var end = checkGameEnd(); if (end.over) { gameEnded = true; stopAllTimers(); if (end.result === 'checkmate') { LK.effects.flashScreen(0x00ff00, 800); if (end.winner === 'white') { LK.showYouWin('White wins by checkmate!'); } else if (end.winner === 'black') { LK.showYouWin('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'); } return; } // --- AI Move for Black --- if (currentTurn === 'black' && !gameEnded) { // --- AI: If an important piece is threatened, try to move it away --- // Define important pieces var importantTypes = { queen: true, rook: true, bishop: true, knight: true }; // Find all black important pieces that are threatened var threatenedPieces = []; for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { var p = board.getPiece(x, y); if (p && p.color === 'black' && importantTypes[p.type]) { if (isAttacked(x, y, 'black')) { threatenedPieces.push({ piece: p, x: x, y: y }); } } } } // If any important piece is threatened, try to move it to safety var aiMoves = getAllLegalMoves('black'); if (threatenedPieces.length > 0) { // Try to find a move that moves a threatened important piece to a safe square var safeMoves = []; for (var i = 0; i < threatenedPieces.length; i++) { var tp = threatenedPieces[i]; var moves = getLegalMoves(tp.piece, tp.x, tp.y, false); for (var j = 0; j < moves.length; j++) { var m = moves[j]; // Simulate the move var origX = tp.piece.boardX, origY = tp.piece.boardY; var origTo = board.getPiece(m.x, m.y); board.squares[tp.y][tp.x] = null; board.squares[m.y][m.x] = tp.piece; tp.piece.boardX = m.x; tp.piece.boardY = m.y; var stillAttacked = isAttacked(m.x, m.y, 'black'); // Undo board.squares[tp.y][tp.x] = tp.piece; board.squares[m.y][m.x] = origTo; tp.piece.boardX = origX; tp.piece.boardY = origY; if (!stillAttacked) { // Find the corresponding move in aiMoves for (var k = 0; k < aiMoves.length; k++) { var am = aiMoves[k]; if (am.from.x === tp.x && am.from.y === tp.y && am.to.x === m.x && am.to.y === m.y) { safeMoves.push(am); } } } } } // If there are safe moves, pick one randomly and play it if (safeMoves.length > 0) { var chosen = safeMoves[Math.floor(Math.random() * safeMoves.length)]; var piece = board.getPiece(chosen.from.x, chosen.from.y); var delay = 400; if (chosen.special === 'castle_kingside' || chosen.special === 'castle_queenside') delay = 900;else if (piece && piece.type === 'pawn' && (chosen.to.y === 0 || chosen.to.y === 7)) delay = 900;else if (board.getPiece(chosen.to.x, chosen.to.y)) delay = 700;else delay = 350; LK.setTimeout(function () { if (piece) { doMove(piece, chosen.from.x, chosen.from.y, chosen.to.x, chosen.to.y, chosen.special); } }, delay); return; } // If no safe move, fallback to normal AI below } // --- AI tries to mimic player's last move style --- // We'll try to find a move for black that is similar in type to the player's last move. // If not possible, fallback to best move as before. // Helper: get move type string var getMoveType = function getMoveType(move) { if (!move) return ''; if (move.special === 'castle_kingside' || move.special === 'castle_queenside') return 'castle'; if (move.special === 'enpassant') return 'enpassant'; if (move.piece && move.piece.type === 'pawn' && (move.to.y === 0 || move.to.y === 7)) return 'promotion'; if (move.captured) return 'capture'; if (move.piece && move.piece.type === 'pawn') return 'pawn'; if (move.piece && move.piece.type === 'knight') return 'knight'; if (move.piece && move.piece.type === 'bishop') return 'bishop'; if (move.piece && move.piece.type === 'rook') return 'rook'; if (move.piece && move.piece.type === 'queen') return 'queen'; if (move.piece && move.piece.type === 'king') return 'king'; return ''; }; // Helper: get move distance (for delay) var getMoveDistance = function getMoveDistance(move) { if (!move) return 1; var dx = Math.abs(move.from.x - move.to.x); var dy = Math.abs(move.from.y - move.to.y); return Math.max(dx, dy); }; // Get all legal moves for black // aiMoves already defined above if (aiMoves.length > 0) { // Use a simple minimax (depth 2) with material evaluation var evaluateBoard = function evaluateBoard() { var values = { pawn: 1, knight: 3, bishop: 3, rook: 5, queen: 9, king: 0 }; var score = 0; for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { var p = board.getPiece(x, y); if (p) { var val = values[p.type] || 0; score += p.color === 'white' ? val : -val; } } } return score; }; var simulateMove = function simulateMove(move) { var piece = board.getPiece(move.from.x, move.from.y); var target = board.getPiece(move.to.x, move.to.y); var fromX = move.from.x, fromY = move.from.y, toX = move.to.x, toY = move.to.y; var special = move.special; var origHasMoved = piece.hasMoved; var origEnPassant = enPassantTarget; var captured = null; // Handle en passant if (special === 'enpassant') { var dir = piece.color === 'white' ? -1 : 1; var capY = toY - dir; captured = board.getPiece(toX, capY); board.squares[capY][toX] = null; } // Handle castling var rook = null, rookFrom = null, rookTo = null; if (special === 'castle_kingside') { var row = piece.color === 'white' ? 7 : 0; rook = board.getPiece(7, row); board.squares[row][5] = rook; board.squares[row][7] = null; } if (special === 'castle_queenside') { var row = piece.color === 'white' ? 7 : 0; rook = board.getPiece(0, row); board.squares[row][3] = rook; board.squares[row][0] = null; } // Remove captured piece if (target && target.color !== piece.color) { board.squares[toY][toX] = null; } // Move piece board.squares[fromY][fromX] = null; board.squares[toY][toX] = piece; piece.hasMoved = true; // Pawn promotion (always promote to queen for AI) var promoted = false; var oldType = piece.type; if (piece.type === 'pawn' && (toY === 0 || toY === 7)) { piece.type = 'queen'; promoted = true; } return function undo() { if (promoted) piece.type = oldType; // Undo move board.squares[fromY][fromX] = piece; board.squares[toY][toX] = target; piece.hasMoved = origHasMoved; // Undo en passant if (special === 'enpassant') { var dir = piece.color === 'white' ? -1 : 1; var capY = toY - dir; board.squares[capY][toX] = captured; } // Undo castling if (special === 'castle_kingside') { var row = piece.color === 'white' ? 7 : 0; board.squares[row][7] = board.squares[row][5]; board.squares[row][5] = null; } if (special === 'castle_queenside') { var row = piece.color === 'white' ? 7 : 0; board.squares[row][0] = board.squares[row][3]; board.squares[row][3] = null; } enPassantTarget = origEnPassant; }; }; // Try to mimic player's last move var mimicMove = null; var mimicType = getMoveType(lastMove); var mimicPieceType = lastMove && lastMove.piece ? lastMove.piece.type : null; var mimicCapture = lastMove && lastMove.captured ? true : false; var mimicSpecial = lastMove ? lastMove.special : null; // Find all black moves that match the last move's type var mimicCandidates = []; for (var i = 0; i < aiMoves.length; i++) { var move = aiMoves[i]; var piece = board.getPiece(move.from.x, move.from.y); if (!piece) continue; // Try to match special moves if (mimicSpecial && move.special === mimicSpecial) { mimicCandidates.push(move); continue; } // Try to match captures if (mimicCapture) { var target = board.getPiece(move.to.x, move.to.y); if (target && target.color === 'white') { mimicCandidates.push(move); continue; } } // Try to match piece type if (mimicPieceType && piece.type === mimicPieceType) { mimicCandidates.push(move); continue; } // Try to match move type if (getMoveType({ piece: piece, to: move.to, special: move.special, captured: board.getPiece(move.to.x, move.to.y) }) === mimicType) { mimicCandidates.push(move); continue; } } // If we have mimic candidates, pick the best among them using minimax var candidateMoves = mimicCandidates.length > 0 ? mimicCandidates : aiMoves; // Minimax, depth 2 (AI: black, Player: white) var bestScore = null; var bestMoves = []; for (var i = 0; i < candidateMoves.length; i++) { var move = candidateMoves[i]; var undo = simulateMove(move); // After black's move, get all white replies var whiteMoves = getAllLegalMoves('white'); var worstScore = null; if (whiteMoves.length === 0) { // If white has no moves, checkmate or stalemate if (isInCheck('white')) { // Black wins worstScore = -9999; } else { // Draw worstScore = 0; } } else { for (var j = 0; j < whiteMoves.length; j++) { var wmove = whiteMoves[j]; var wundo = simulateMove(wmove); var score = evaluateBoard(); if (worstScore === null || score > worstScore) { worstScore = score; } wundo(); } } undo(); // AI wants to minimize the score (since black is negative) if (bestScore === null || worstScore < bestScore) { bestScore = worstScore; bestMoves = [move]; } else if (worstScore === bestScore) { bestMoves.push(move); } } // Pick randomly among best moves var chosen = bestMoves[Math.floor(Math.random() * bestMoves.length)]; var piece = board.getPiece(chosen.from.x, chosen.from.y); // Add a delay based on move complexity (longer for captures, promotions, castling, or long moves) var delay = 400; if (chosen.special === 'castle_kingside' || chosen.special === 'castle_queenside') delay = 900;else if (piece && piece.type === 'pawn' && (chosen.to.y === 0 || chosen.to.y === 7)) delay = 900;else if (board.getPiece(chosen.to.x, chosen.to.y)) delay = 700;else delay = 350 + 80 * getMoveDistance(chosen); LK.setTimeout(function () { if (piece) { doMove(piece, chosen.from.x, chosen.from.y, chosen.to.x, chosen.to.y, chosen.special); } }, delay); } } } // --- 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; } // Reset timers stopAllTimers(); whiteTime = 600; blackTime = 600; whiteTimerText.setText(formatTime(whiteTime)); blackTimerText.setText(formatTime(blackTime)); startWhiteTimer(); });
===================================================================
--- original.js
+++ change.js
@@ -230,14 +230,14 @@
/****
* Game Code
****/
-// Crown for promoted pawn (queen)
-// Black pieces
-// White pieces
-// Board squares
-// We need tween for piece movement animations
// --- Board and Piece Sizes ---
+// We need tween for piece movement animations
+// Board squares
+// White pieces
+// Black pieces
+// Crown for promoted pawn (queen)
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);
@@ -899,8 +899,10 @@
board.setPiece(fromX, fromY, null);
board.setPiece(toX, toY, piece);
piece.moveTo(toX, toY, true);
piece.hasMoved = true;
+ // Play move sound
+ LK.getSound('tas').play();
// Pawn promotion
if (piece.type === 'pawn' && (toY === 0 || toY === 7)) {
showPromotionPopup(piece, toX, toY, function (newType) {
piece.type = newType;