/** * Lara Kiesewetter – Live Schachturnier * Haupt-Application */ const PGN_URL = 'https://www.deutsche-schachjugend.de/2026/odjm-d/partien/gesamt-utf8.pgn'; const REFRESH_INTERVAL = 60000; // 60 Sekunden const PLAYER_NAME = 'Kiesewetter, Lara'; let board = null; let chess = null; let currentGame = null; let allLaraGames = []; let refreshTimer = null; let countdown = 0; let serverLastFetch = null; /** * Lädt die PGN-Datei und aktualisiert die Anzeige */ async function loadPGN() { showLoading(true); hideError(); try { const [pgnResponse, statusResponse] = await Promise.all([ fetch('http://localhost:8111/pgn'), fetch('http://localhost:8111/status').catch(() => null) ]); if (!pgnResponse.ok) throw new Error(`HTTP ${pgnResponse.status}`); if (statusResponse && statusResponse.ok) { const status = await statusResponse.json(); serverLastFetch = status.last_fetch ? status.last_fetch * 1000 : null; } const pgnText = await pgnResponse.text(); const allGames = parsePGN(pgnText); allLaraGames = filterLaraGames(allGames); if (allLaraGames.length === 0) { showError('Keine Partien von Lara gefunden.'); return; } // Finde die aktuelle/live Partie const liveGame = getLiveGame(allLaraGames); const targetGame = liveGame || getLatestGame(allLaraGames); currentGame = targetGame; updateBoard(); updatePlayerInfo(); updateMovesList(); updateAllGamesList(); updateTimestamp(); showLoading(false); } catch (error) { console.error('Fehler beim Laden:', error); showError(`Fehler: ${error.message}`); showLoading(false); } } /** * Aktualisiert das Schachbrett */ function updateBoard() { if (!currentGame) return; chess = new Chess(); // Spiegele das Brett, wenn Lara Schwarz hat const laraIsBlack = currentGame.black.toLowerCase().includes('kiesewetter'); const orientation = laraIsBlack ? 'black' : 'white'; // Führe alle Züge aus for (const move of currentGame.moves) { if (move.isResult) break; try { chess.move(move.san); } catch (e) { // Ignoriere ungültige Züge } } if (board) board.position(chess.fen(), true); else { board = Chessboard('board', { position: chess.fen(), orientation: orientation, pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png', draggable: false, spawnMoveError: false }); // Click auf Züge document.getElementById('moves-list').addEventListener('click', handleMoveClick); } // Highlight active player highlightActivePlayer(); } /** * Aktualisiert die Spielerinformationen */ function updatePlayerInfo() { if (!currentGame) return; const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter'); if (laraIsWhite) { document.getElementById('white-name').textContent = currentGame.white; document.getElementById('white-elo').textContent = currentGame.whiteElo ? `(ELO: ${currentGame.whiteElo})` : ''; document.getElementById('white-clock').textContent = formatClock(currentGame.whiteClock); document.getElementById('black-name').textContent = currentGame.black; document.getElementById('black-elo').textContent = currentGame.blackElo ? `(ELO: ${currentGame.blackElo})` : ''; document.getElementById('black-clock').textContent = formatClock(currentGame.blackClock); } else { document.getElementById('white-name').textContent = currentGame.white; document.getElementById('white-elo').textContent = currentGame.whiteElo ? `(ELO: ${currentGame.whiteElo})` : ''; document.getElementById('white-clock').textContent = formatClock(currentGame.whiteClock); document.getElementById('black-name').textContent = currentGame.black; document.getElementById('black-elo').textContent = currentGame.blackElo ? `(ELO: ${currentGame.blackElo})` : ''; document.getElementById('black-clock').textContent = formatClock(currentGame.blackClock); } // Round info const roundInfo = `Runde ${currentGame.round} – ${currentGame.event || 'Turnier'}`; document.getElementById('round-info').textContent = roundInfo; // Result info const resultEl = document.getElementById('result-info'); if (currentGame.isLive) { resultEl.innerHTML = '● Laufen'; } else { resultEl.textContent = `Ergebnis: ${currentGame.result}`; } } /** * Highlight den Spieler, der gerade am Zug ist */ function highlightActivePlayer() { if (!chess) return; const whiteEl = document.getElementById('player-white'); const blackEl = document.getElementById('player-black'); whiteEl.classList.remove('active'); blackEl.classList.remove('active'); if (chess.turn() === 'w') { whiteEl.classList.add('active'); } else { blackEl.classList.add('active'); } } /** * Aktualisiert die Zugliste */ function updateMovesList() { if (!currentGame) return; const movesList = document.getElementById('moves-list'); movesList.innerHTML = ''; const nonResultMoves = currentGame.moves.filter(m => !m.isResult); for (let i = 0; i < nonResultMoves.length; i += 2) { const moveNumber = Math.floor(i / 2) + 1; // Move number const numSpan = document.createElement('span'); numSpan.className = 'move-number'; numSpan.textContent = `${moveNumber}.`; movesList.appendChild(numSpan); // White move const whiteMove = document.createElement('span'); whiteMove.className = 'move'; whiteMove.textContent = nonResultMoves[i].san; whiteMove.dataset.index = i; movesList.appendChild(whiteMove); // Black move if (i + 1 < nonResultMoves.length) { const blackMove = document.createElement('span'); blackMove.className = 'move'; blackMove.textContent = nonResultMoves[i + 1].san; blackMove.dataset.index = i + 1; movesList.appendChild(blackMove); } } // Mark last move if (nonResultMoves.length > 0) { const lastMove = movesList.querySelector(`[data-index="${nonResultMoves.length - 1}"]`); if (lastMove) lastMove.classList.add('current'); } // Scroll to bottom movesList.scrollTop = movesList.scrollHeight; } /** * Klick auf einen Zug in der Liste */ function handleMoveClick(e) { if (!e.target.classList.contains('move') || !currentGame) return; const index = parseInt(e.target.dataset.index); const nonResultMoves = currentGame.moves.filter(m => !m.isResult); chess = new Chess(); for (let i = 0; i <= index; i++) { try { chess.move(nonResultMoves[i].san); } catch (err) { break; } } board.position(chess.fen(), true); // Update current highlight document.querySelectorAll('#moves-list .move').forEach(el => el.classList.remove('current')); e.target.classList.add('current'); } /** * Aktualisiert die Liste aller Partien */ function updateAllGamesList() { const list = document.getElementById('all-games-list'); list.innerHTML = ''; // Sort by round const sorted = [...allLaraGames].sort((a, b) => { return (parseInt(a.round) || 0) - (parseInt(b.round) || 0); }); for (const game of sorted) { const entry = document.createElement('div'); entry.className = 'game-entry'; if (currentGame && game === currentGame) entry.classList.add('active'); const laraIsWhite = game.white.toLowerCase().includes('kiesewetter'); const opponent = laraIsWhite ? game.black : game.white; const color = laraIsWhite ? '⬜ Weiß' : '⬛ Schwarz'; entry.innerHTML = `