Chess Strategy Trainer

Chess Strategy Trainer

Chess Strategy Trainer

Puzzle

Instructions & Controls

Goal:

Puzzle List

These are the puzzles currently loaded in the trainer. (Editing is not currently enabled).

`; cst_puzzlesDisplay.appendChild(puzzleDiv); }); } // --- DASHBOARD (TRAINER) FUNCTIONS --- /** * Renders the chessboard based on cst_boardState */ function cst_renderBoard() { if (!cst_board) return; cst_board.innerHTML = ''; // Clear board for (let rank = 8; rank >= 1; rank--) { for (let fileIndex = 0; fileIndex < 8; fileIndex++) { const file = FILES[fileIndex]; const squareId = `${file}${rank}`; const square = document.createElement('div'); square.id = squareId; square.className = `cst-square ${(fileIndex + rank) % 2 === 0 ? 'dark' : 'light'}`; square.addEventListener('click', cst_handleSquareClick); const piece = cst_boardState[squareId]; if (piece) { const pieceSpan = document.createElement('span'); pieceSpan.textContent = PIECE_UNICODE[piece] || '?'; pieceSpan.dataset.piece = piece; // Store piece info square.appendChild(pieceSpan); } cst_board.appendChild(square); } } } /** * Loads and displays the next puzzle */ function cst_loadNextPuzzle() { cst_currentPuzzleIndex = (cst_currentPuzzleIndex + 1) % cst_puzzles.length; cst_loadPuzzle(cst_currentPuzzleIndex); } /** * Loads a specific puzzle by index */ function cst_loadPuzzle(index) { if (index < 0 || index >= cst_puzzles.length) return; const puzzle = cst_puzzles[index]; cst_currentPuzzleIndex = index; const { board, turn } = cst_parseFEN(puzzle.fen); cst_boardState = board; cst_turn = turn; cst_selectedSquare = null; cst_gameState = 'idle'; cst_puzzleDescription.textContent = puzzle.description; cst_puzzleTurn.textContent = `Turn: ${turn === 'w' ? 'White' : 'Black'}`; cst_feedbackArea.textContent = ''; cst_feedbackArea.className = 'h-12 text-center text-xl font-bold mb-4'; // Reset classes cst_renderBoard(); } /** * Handles clicking on a square */ function cst_handleSquareClick(event) { const square = event.currentTarget; const squareId = square.id; const piece = cst_boardState[squareId]; const pieceColor = piece ? piece[0] : null; if (cst_gameState === 'solved' || cst_gameState === 'failed') return; // Don't allow moves after puzzle end // 1. Selecting a piece to move if (!cst_selectedSquare && piece && pieceColor === cst_turn) { cst_selectedSquare = squareId; cst_gameState = 'selecting_move'; cst_highlightSelected(squareId); // Future enhancement: Highlight possible moves } // 2. Selecting destination square (or deselecting) else if (cst_selectedSquare) { // Deselecting if (cst_selectedSquare === squareId) { cst_selectedSquare = null; cst_gameState = 'idle'; cst_clearHighlights(); } // Selecting destination else { cst_makeMove(cst_selectedSquare, squareId); cst_clearHighlights(); cst_selectedSquare = null; cst_gameState = 'idle'; } } } /** * Highlights the selected square */ function cst_highlightSelected(squareId) { cst_clearHighlights(); const square = document.getElementById(squareId); if (square) square.classList.add('selected'); } /** * Clears all square highlights */ function cst_clearHighlights() { cst_board.querySelectorAll('.cst-square').forEach(sq => { sq.classList.remove('selected', 'highlight-move'); }); } /** * Processes a move attempt */ function cst_makeMove(fromSq, toSq) { const puzzle = cst_puzzles[cst_currentPuzzleIndex]; const expectedMove = puzzle.solution[0]; // For now, only check the first move const userMove = fromSq + toSq; if (userMove === expectedMove) { cst_feedbackArea.textContent = "Correct!"; cst_feedbackArea.className = 'h-12 text-center text-xl font-bold mb-4 text-green-600'; cst_gameState = 'solved'; // Update board state visually (simple move, no capture logic needed for these puzzles yet) const piece = cst_boardState[fromSq]; delete cst_boardState[fromSq]; cst_boardState[toSq] = piece; cst_renderBoard(); // Re-render to show the move } else { cst_feedbackArea.textContent = `Incorrect. The solution was ${expectedMove}.`; cst_feedbackArea.className = 'h-12 text-center text-xl font-bold mb-4 text-red-600'; cst_gameState = 'failed'; } } /** * Shows the solution move */ function cst_showSolution() { if (cst_currentPuzzleIndex === -1) return; const puzzle = cst_puzzles[cst_currentPuzzleIndex]; const solution = puzzle.solution.join(', '); // Join if multiple moves later cst_feedbackArea.textContent = `Solution: ${solution}`; cst_feedbackArea.className = 'h-12 text-center text-xl font-bold mb-4 text-blue-600'; cst_gameState = 'failed'; // Mark as failed/ended if solution shown } /** * Renders the current puzzle state for PDF */ function cst_renderPdfClone() { const puzzle = cst_puzzles[cst_currentPuzzleIndex]; if (!puzzle) return; cst_pdfRenderClone.innerHTML = `

Chess Puzzle

Goal:

${cst_escapeHTML(puzzle.description)}

Turn: ${puzzle.turn === 'w' ? 'White' : 'Black'}

Initial Position:

Solution:

${puzzle.solution.join(', ')}

`; // Render the board into the clone const pdfBoard = cst_pdfRenderClone.querySelector('#pdf-board'); if(pdfBoard) { // Temporarily set board state to initial puzzle state const currentBoardState = { ...cst_boardState }; // Backup current state const { board: initialBoard } = cst_parseFEN(puzzle.fen); cst_boardState = initialBoard; // Render into pdfBoard pdfBoard.innerHTML = ''; for (let rank = 8; rank >= 1; rank--) { for (let fileIndex = 0; fileIndex < 8; fileIndex++) { const file = FILES[fileIndex]; const squareId = `${file}${rank}`; const square = document.createElement('div'); square.className = `cst-square ${(fileIndex + rank) % 2 === 0 ? 'dark' : 'light'}`; const piece = cst_boardState[squareId]; if (piece) { const pieceSpan = document.createElement('span'); pieceSpan.textContent = PIECE_UNICODE[piece] || '?'; square.appendChild(pieceSpan); } pdfBoard.appendChild(square); } } // Restore board state cst_boardState = currentBoardState; } } /** * Generates and downloads a PDF of the current puzzle */ async function cst_downloadPDF() { if (cst_currentPuzzleIndex === -1) { alert("Please load a puzzle first."); return; } if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { console.error("CST Tool Error: jsPDF or html2canvas library not loaded."); alert("Error: PDF libraries failed to load. Please check console."); return; } cst_renderPdfClone(); // Create and populate the clone const { jsPDF } = window.jspdf; try { const canvas = await html2canvas(cst_pdfRenderClone, { scale: 2, useCORS: true, }); const imgData = canvas.toDataURL('image/png'); const imgWidth = canvas.width; const imgHeight = canvas.height; const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const ratio = Math.min((pdfWidth - 80) / imgWidth, (pdfHeight - 80) / imgHeight); const w = imgWidth * ratio; const h = imgHeight * ratio; const x = (pdfWidth - w) / 2; const y = 40; // Top margin pdf.addImage(imgData, 'PNG', x, y, w, h); pdf.save(`Chess_Puzzle_${cst_currentPuzzleIndex + 1}.pdf`); } catch (error) { console.error("CST Tool Error: PDF generation failed.", error); alert("An error occurred while generating the PDF. Please try again."); } } // --- EVENT LISTENERS --- // Tab link clicks cst_tabLinks.forEach((link, index) => { link.addEventListener('click', () => cst_switchTab(index)); }); // Next/Prev button clicks if (cst_prevButton) { cst_prevButton.addEventListener('click', () => { if (cst_currentTab > 0) cst_switchTab(cst_currentTab - 1); }); } if (cst_nextButton) { cst_nextButton.addEventListener('click', () => { if (cst_currentTab < cst_tabLinks.length - 1) cst_switchTab(cst_currentTab + 1); }); } // PDF download if (cst_downloadPdfButton) { cst_downloadPdfButton.addEventListener('click', cst_downloadPDF); } // Dashboard Controls if (cst_nextPuzzleButton) { cst_nextPuzzleButton.addEventListener('click', cst_loadNextPuzzle); } if (cst_showSolutionButton) { cst_showSolutionButton.addEventListener('click', cst_showSolution); } if (cst_resetAttemptButton) { cst_resetAttemptButton.addEventListener('click', () => cst_loadPuzzle(cst_currentPuzzleIndex)); // Reload current } // --- INITIALIZATION --- cst_loadPuzzles(); cst_loadNextPuzzle(); // Load the first puzzle cst_renderConfig(); // Populate config tab on load // Set initial tab state cst_tabPanes.forEach((pane, index) => { pane.classList.toggle('hidden', index !== 0); pane.classList.toggle('cst-active', index === 0); }); });