function initMinesweeperGame()

in src/gemini_95/index.ts [1176:1563]


function initMinesweeperGame(windowElement: HTMLDivElement): void {
    console.log("Initializing Minesweeper...");
    const boardElement = windowElement.querySelector('#minesweeper-board') as HTMLDivElement;
    const flagCountElement = windowElement.querySelector('.minesweeper-flag-count') as HTMLDivElement;
    const timerElement = windowElement.querySelector('.minesweeper-timer') as HTMLDivElement;
    const resetButton = windowElement.querySelector('.minesweeper-reset-button') as HTMLButtonElement;
    // >>> Get Hint elements <<< //
    const hintButton = windowElement.querySelector('.minesweeper-hint-button') as HTMLButtonElement;
    const commentaryElement = windowElement.querySelector('.minesweeper-commentary') as HTMLDivElement;

    if (!boardElement || !flagCountElement || !timerElement || !resetButton || !hintButton || !commentaryElement) { // Add hint elements to check
        console.error("Minesweeper UI elements not found!");
        return;
    }

    let grid: MinesweeperCell[][] = [];

    function resetGame() {
        console.log("Resetting Minesweeper game.");
        // Reset state variables
        if (minesweeperTimerInterval) {
            clearInterval(minesweeperTimerInterval);
            minesweeperTimerInterval = null;
        }
        minesweeperTimeElapsed = 0;
        minesweeperFlagsPlaced = 0;
        minesweeperGameOver = false;
        minesweeperFirstClick = true;
        minesweeperMineCount = 10;
        minesweeperGridSize = { rows: 9, cols: 9 };

        // Update UI
        timerElement.textContent = `⏱️ 0`;
        flagCountElement.textContent = `🚩 ${minesweeperMineCount}`; // Show total mines initially
        resetButton.textContent = '🙂';

        // Create the grid
        createGrid();
    }

    function createGrid() {
        boardElement.innerHTML = ''; // Clear previous grid
        grid = []; // Reset internal grid state
        boardElement.style.gridTemplateColumns = `repeat(${minesweeperGridSize.cols}, 20px)`;
        boardElement.style.gridTemplateRows = `repeat(${minesweeperGridSize.rows}, 20px)`;

        for (let r = 0; r < minesweeperGridSize.rows; r++) {
            const row: MinesweeperCell[] = [];
            for (let c = 0; c < minesweeperGridSize.cols; c++) {
                const cellElement = document.createElement('div');
                cellElement.classList.add('minesweeper-cell');

                const cellData: MinesweeperCell = {
                    isMine: false,
                    isRevealed: false,
                    isFlagged: false,
                    adjacentMines: 0,
                    element: cellElement,
                    row: r,
                    col: c,
                };

                cellElement.addEventListener('click', () => handleCellClick(cellData));
                cellElement.addEventListener('contextmenu', (e) => {
                    e.preventDefault();
                    handleCellRightClick(cellData);
                });

                row.push(cellData);
                boardElement.appendChild(cellElement);
            }
            grid.push(row);
        }
        console.log(`Grid created (${minesweeperGridSize.rows}x${minesweeperGridSize.cols})`);

        // Mines will be placed on the first click
    }

    function placeMines(firstClickRow: number, firstClickCol: number) {
        console.log(`Placing ${minesweeperMineCount} mines, avoiding ${firstClickRow},${firstClickCol}`);
        let minesPlaced = 0;
        while (minesPlaced < minesweeperMineCount) {
            const r = Math.floor(Math.random() * minesweeperGridSize.rows);
            const c = Math.floor(Math.random() * minesweeperGridSize.cols);

            // Don't place a mine on the first clicked cell or if it already has a mine
            if ((r === firstClickRow && c === firstClickCol) || grid[r][c].isMine) {
                continue;
            }

            grid[r][c].isMine = true;
            minesPlaced++;
        }

        // Calculate adjacent mines for all cells
        for (let r = 0; r < minesweeperGridSize.rows; r++) {
            for (let c = 0; c < minesweeperGridSize.cols; c++) {
                if (!grid[r][c].isMine) {
                    grid[r][c].adjacentMines = countAdjacentMines(r, c);
                }
            }
        }
        console.log("Mines placed and adjacent counts calculated.");
    }

    function countAdjacentMines(row: number, col: number): number {
        let count = 0;
        for (let dr = -1; dr <= 1; dr++) {
            for (let dc = -1; dc <= 1; dc++) {
                if (dr === 0 && dc === 0) continue; // Skip self
                const nr = row + dr;
                const nc = col + dc;

                if (
                    nr >= 0 && nr < minesweeperGridSize.rows &&
                    nc >= 0 && nc < minesweeperGridSize.cols &&
                    grid[nr][nc].isMine
                ) {
                    count++;
                }
            }
        }
        return count;
    }

    function handleCellClick(cell: MinesweeperCell) {
        if (minesweeperGameOver || cell.isRevealed || cell.isFlagged) {
            return;
        }

        // Start timer on first click
        if (minesweeperFirstClick && !minesweeperTimerInterval) {
             // Place mines *after* knowing the first click location
             placeMines(cell.row, cell.col);
             minesweeperFirstClick = false;
             startTimer();
        }

        if (cell.isMine) {
            gameOver(cell); // Pass the clicked mine
        } else {
            revealCell(cell);
            checkWinCondition(); // Check win after revealing
        }
    }

    function handleCellRightClick(cell: MinesweeperCell) {
        if (minesweeperGameOver || cell.isRevealed) {
            return;
        }

        if (!minesweeperFirstClick && !minesweeperTimerInterval) {
            // Prevent flagging before the game starts (timer starts)
            return;
        }

        cell.isFlagged = !cell.isFlagged;
        cell.element.textContent = cell.isFlagged ? '🚩' : '';

        // Update flag count display
        if (cell.isFlagged) {
            minesweeperFlagsPlaced++;
        } else {
            minesweeperFlagsPlaced--;
        }
        updateFlagCount();

        checkWinCondition(); // Check if flagging the last mine wins
    }

    function revealCell(cell: MinesweeperCell) {
        if (cell.isRevealed || cell.isFlagged || cell.isMine) {
            return; // Should not happen if called correctly, but safe check
        }

        cell.isRevealed = true;
        cell.element.classList.add('revealed');
        cell.element.textContent = ''; // Clear flag if it was mistakenly revealed

        if (cell.adjacentMines > 0) {
            cell.element.textContent = cell.adjacentMines.toString();
            cell.element.dataset.number = cell.adjacentMines.toString(); // For CSS coloring
        } else {
            // Flood fill for empty cells
            for (let dr = -1; dr <= 1; dr++) {
                for (let dc = -1; dc <= 1; dc++) {
                    if (dr === 0 && dc === 0) continue;
                    const nr = cell.row + dr;
                    const nc = cell.col + dc;

                    if (
                        nr >= 0 && nr < minesweeperGridSize.rows &&
                        nc >= 0 && nc < minesweeperGridSize.cols
                    ) {
                        const neighbor = grid[nr][nc];
                        if (!neighbor.isRevealed && !neighbor.isFlagged) {
                            // Delay slightly to show cascade effect (optional)
                            // setTimeout(() => revealCell(neighbor), 10);
                            revealCell(neighbor); // Recursive call
                        }
                    }
                }
            }
        }
    }

    function startTimer() {
        if (minesweeperTimerInterval) return; // Already running
        minesweeperTimeElapsed = 0;
        timerElement.textContent = `⏱️ ${minesweeperTimeElapsed}`;
        minesweeperTimerInterval = window.setInterval(() => {
            minesweeperTimeElapsed++;
            timerElement.textContent = `⏱️ ${minesweeperTimeElapsed}`;
        }, 1000);
        console.log("Minesweeper timer started.");
    }

    function updateFlagCount() {
        const remainingFlags = minesweeperMineCount - minesweeperFlagsPlaced;
        flagCountElement.textContent = `🚩 ${remainingFlags}`;
    }

    function gameOver(clickedMine: MinesweeperCell) {
        console.log("Game Over!");
        minesweeperGameOver = true;
        if (minesweeperTimerInterval) {
            clearInterval(minesweeperTimerInterval);
            minesweeperTimerInterval = null;
        }
        resetButton.textContent = '😵';

        // Reveal all mines
        grid.forEach(row => {
            row.forEach(cell => {
                if (cell.isMine) {
                    cell.element.classList.add('mine');
                    cell.element.textContent = '💣';
                    if (cell !== clickedMine) { // Don't override the exploded one
                       cell.element.classList.add('revealed'); // Show the bomb
                    }
                }
                // Optionally show incorrectly placed flags
                if (!cell.isMine && cell.isFlagged) {
                    cell.element.textContent = '❌';
                }
            });
        });

        // Highlight the mine that was clicked
        clickedMine.element.classList.add('exploded');
        clickedMine.element.textContent = '💥';
    }

    function checkWinCondition() {
        if (minesweeperGameOver) return;

        let revealedCount = 0;
        let correctlyFlaggedMines = 0;

        for (let r = 0; r < minesweeperGridSize.rows; r++) {
            for (let c = 0; c < minesweeperGridSize.cols; c++) {
                const cell = grid[r][c];
                if (cell.isRevealed && !cell.isMine) {
                    revealedCount++;
                }
                if (cell.isFlagged && cell.isMine) {
                    correctlyFlaggedMines++;
                }
            }
        }

        const totalNonMineCells = (minesweeperGridSize.rows * minesweeperGridSize.cols) - minesweeperMineCount;
        const allNonMinesRevealed = revealedCount === totalNonMineCells;
        const allMinesFlagged = correctlyFlaggedMines === minesweeperMineCount && minesweeperFlagsPlaced === minesweeperMineCount;

        if (allNonMinesRevealed || allMinesFlagged) {
            console.log("Game Won!");
            minesweeperGameOver = true;
            if (minesweeperTimerInterval) {
                clearInterval(minesweeperTimerInterval);
                minesweeperTimerInterval = null;
            }
            resetButton.textContent = '😎';
            // Optionally auto-flag remaining mines if won by revealing
            if (allNonMinesRevealed) {
                 grid.forEach(row => row.forEach(cell => {
                     if (cell.isMine && !cell.isFlagged) {
                         cell.isFlagged = true;
                         cell.element.textContent = '🚩';
                         minesweeperFlagsPlaced++;
                     }
                 }));
                 updateFlagCount();
            }
        }
    }

    // >>> Function to get board state as text <<< //
    function getBoardStateAsText(): string {
        let boardString = "Current Minesweeper Board:\n";
        boardString += `Flags Remaining: ${minesweeperMineCount - minesweeperFlagsPlaced}\n`;
        boardString += `Time Elapsed: ${minesweeperTimeElapsed}s\n`;
        boardString += "Grid (H=Hidden, F=Flagged, B=Bomb, Number=Adjacent Mines):\n";
        for (let r = 0; r < minesweeperGridSize.rows; r++) {
            let rowStr = "";
            for (let c = 0; c < minesweeperGridSize.cols; c++) {
                const cell = grid[r][c];
                if (cell.isFlagged) {
                    rowStr += " F ";
                } else if (!cell.isRevealed) {
                    rowStr += " H ";
                } else if (cell.isMine) { // Should only show if game over, but include for context
                    rowStr += " B ";
                } else if (cell.adjacentMines > 0) {
                    rowStr += ` ${cell.adjacentMines} `;
                } else { // Revealed empty cell
                    rowStr += " _ ";
                }
            }
            boardString += rowStr + "\n";
        }
        return boardString;
    }

    // >>> Function to get AI Hint <<< //
    async function getAiHint() {
        if (minesweeperGameOver || minesweeperFirstClick) {
            commentaryElement.textContent = "Click a square first!";
            return;
        }

        hintButton.disabled = true;
        hintButton.textContent = '🤔';
        commentaryElement.textContent = 'Thinking...';

        // --- Initialize Gemini using the required pattern --- //
        if (!geminiInstance) {
            if (!await initializeGeminiIfNeeded('getAiHint')) {
                commentaryElement.textContent = 'AI Init Error: Could not connect to Gemini';
                hintButton.disabled = false;
                hintButton.textContent = '💡 Hint';
                return;
            }
        }
        // --- End Gemini Initialization ---

        try {
            const boardState = getBoardStateAsText();
            const prompt = `
You are a witty, slightly sarcastic Minesweeper expert playing along.
Based on the following Minesweeper board state, provide a short (1-2 sentence) hint or observation about a potentially safe move or a dangerous area. Don't give away exact mine locations unless it's logically certain from the revealed numbers. Format the hint as playful commentary.

${boardState}
Hint:`;

            // --- Call Gemini API using existing pattern --- //
            const result = await geminiInstance.models.generateContent({
                model: "gemini-1.5-flash", // Use a fast model
                contents: [{ role: "user", parts: [{ text: prompt }] }],
                config: {
                    temperature: 0.7, // Allow for some creativity
                }
            });

            const hintText = result?.candidates?.[0]?.content?.parts?.[0]?.text?.trim() || "My circuits are buzzing... maybe try clicking somewhere?";
            commentaryElement.textContent = hintText;
            console.log("Minesweeper Hint:", hintText);

        } catch (error) {
            console.error("Error getting Minesweeper hint:", error);
            let errorMessage = 'Unknown error';
             if (error instanceof Error) errorMessage = error.message;
             else try {errorMessage = JSON.stringify(error); } catch {} // Simple stringify
            commentaryElement.textContent = `Hint Error: ${errorMessage}`;
        } finally {
            hintButton.disabled = false;
            hintButton.textContent = '💡 Hint';
        }

    }

    // --- Event Listeners --- //
    resetButton.addEventListener('click', resetGame);
    hintButton.addEventListener('click', getAiHint); // <<< Add listener for hint button

    // Initial setup
    resetGame();
}