diff --git a/frontend/src/lib/components/ChessGameOverlay.svelte b/frontend/src/lib/components/ChessGameOverlay.svelte index 2664751..685c2ef 100644 --- a/frontend/src/lib/components/ChessGameOverlay.svelte +++ b/frontend/src/lib/components/ChessGameOverlay.svelte @@ -13,6 +13,39 @@ let legalMoves = []; let moveHistory = []; + // Convert Chess960 FEN to standard FEN for chess.js compatibility + // Chess960 uses file-based castling (e.g., "HAha") instead of "KQkq" + function convertChess960Fen(fen) { + if (!fen) return fen; + const parts = fen.split(' '); + if (parts.length < 3) return fen; + + // Check if castling field contains Chess960 notation (letters other than KQkq-) + const castling = parts[2]; + if (/^[KQkq-]+$/.test(castling)) { + return fen; // Already standard notation + } + + // Convert Chess960 castling to standard: any uppercase = KQ rights, any lowercase = kq rights + let newCastling = ''; + if (/[A-H]/.test(castling)) { + // Has white castling rights - simplified to KQ + const whiteFiles = castling.match(/[A-H]/g) || []; + if (whiteFiles.length >= 2) newCastling += 'KQ'; + else if (whiteFiles.length === 1) newCastling += 'K'; + } + if (/[a-h]/.test(castling)) { + // Has black castling rights - simplified to kq + const blackFiles = castling.match(/[a-h]/g) || []; + if (blackFiles.length >= 2) newCastling += 'kq'; + else if (blackFiles.length === 1) newCastling += 'k'; + } + if (!newCastling) newCastling = '-'; + + parts[2] = newCastling; + return parts.join(' '); + } + // Match state from overlay store let myColor = null; @@ -73,8 +106,8 @@ try { const chessModule = await import('chess.js'); Chess = chessModule.Chess; - // Don't initialize with default position - we'll load Chess960 FEN later - game = new Chess(); + // Don't initialize game yet - we'll create with Chess960 FEN when received + game = null; // Set up match event handler console.log('[ChessOverlay] Registering match event handler...'); @@ -126,9 +159,9 @@ if (payload.status === 'waiting') { myColor = payload.yourColor; - // Load the Chess960 starting position - if (game && payload.fen) { - game.load(payload.fen, { chess960: true }); + // Create Chess960 game with the starting position + if (Chess && payload.fen) { + game = new Chess(convertChess960Fen(payload.fen)); updateBoardDisplay(); } @@ -143,8 +176,9 @@ const session = nakama.getSession(); myColor = payload.whiteId === session?.user_id ? 'w' : 'b'; - if (game) { - game.load(payload.fen, { chess960: true }); + // Create Chess960 game with current position + if (Chess && payload.fen) { + game = new Chess(convertChess960Fen(payload.fen)); } gamesOverlay.setMode('playing'); @@ -163,8 +197,9 @@ } else if (payload.status === 'spectating') { myColor = null; // Spectator has no color - if (game) { - game.load(payload.fen, { chess960: true }); + // Create Chess960 game with current position + if (Chess && payload.fen) { + game = new Chess(convertChess960Fen(payload.fen)); } gamesOverlay.setMode('spectating'); @@ -184,7 +219,7 @@ function handleOpponentMove(payload) { if (game) { - game.load(payload.fen, { chess960: true }); + game.load(convertChess960Fen(payload.fen)); } moveHistory = [...moveHistory, payload.move];