Code viewer for World: Puzzle Chess Game
//=== EXPLANATIONS =============================================================

//This project has enabled the creation of a platform for mini chess games based
//on learning by observing moves played by artificial intelligences, 
//attempting to reproduce them, and analyzing games played by these same AIs.

//==============================================================================

//===== RULES ==================================================================
//The rules have been implemented following the chessboardjs.com documentation: 
//https://www.chessboardjs.com/
//I only modified the onDrop function to create its own rules according 
//to the mini game being played.
//==============================================================================

var whiteSquareGrey = '#a9a9a9';
var blackSquareGrey = '#696969';

function removeGreySquares (name) {
    $('#' + name + ' .square-55d63').css('background', '');
}

function greySquare (square, name) {
    var $square = $('#' + name + ' .square-' + square);
    
    var background = whiteSquareGrey;
    if ($square.hasClass('black-3c85d')) {
        background = blackSquareGrey;
    }
    
    $square.css('background', background);
}

function onDragStart (source, piece, game) {
    // do not pick up pieces if the game is over
    if (game.game_over()) return false;
    
    // or if it's not that side's turn
    if ((game.turn() === 'w' && piece.search(/^b/) !== -1) ||
        (game.turn() === 'b' && piece.search(/^w/) !== -1)) {
    return false;
    }
}

function onDrop (source, target, board, name, game) {
    
    if (game.mode == 'menu') {
        moveMenu(source, target, board, name, game);
    }
    
    else if (game.mode == 'puzzle') {
        if (typeof board.currentIndexPuzzle !== "undefined" && board.sequencePuzzle) {
            movePuzzle(source, target, board, name, game);
        }
    }
    
    else if (game.mode == 'analysis' || game.mode == 'navigation') {
        return 'snapback';
    }
    
    else {
        
        removeGreySquares(name);
        
        // see if the move is legal
        var userMove = game.move({
            from: source,
            to: target,
            promotion: 'q' // NOTE: always promote to a queen for example simplicity
        });
        
        // illegal move
        if (userMove === null) return 'snapback';
    }
}

function onMouseoverSquare (square, piece, name, game) {
    if (game.game_over()) return;

    // get list of possible moves for this square
    var moves = game.moves({
        square: square,
        verbose: true
    });
    
    // exit if there are no moves available for this square
    if (moves.length === 0) return;
    
    // highlight the square they moused over
    greySquare(square, name);
    
    // highlight the possible squares for this piece
    for (var i = 0; i < moves.length; i++) {
        greySquare(moves[i].to, name);
    }
}

function onMouseoutSquare (square, piece, name) {
    removeGreySquares(name);
}

function onSnapEnd (board, game) {
    board.position(game.fen());
}

//==============================================================================

//===== CHESSBOARD IMPORT ======================================================

//This function allows you to create a chessboard and a game linked to the board, 
//using the Chessboard and Chess js libraries.

function initBoard(name) {
    
    let game = new Chess();
    game.mode = 'menu';
    
    let board = Chessboard(name, {
        draggable: true,
        position: 'start',
        onDragStart: function (source, piece, position, orientation) {
            return onDragStart(source, piece, game);
        },
        onDrop: function (source, target) {
            return onDrop(source, target, board, name, game);
        },
        onMouseoutSquare: function (square, piece) {
            return onMouseoutSquare(square, piece, name);
        },
        onMouseoverSquare: function (square, piece) {
            return onMouseoverSquare(square, piece, name, game);
        },
        onSnapEnd: function () {
            return onSnapEnd(board, game);
        },
        pieceTheme: '/uploads/korentin/{piece}.png',
    });
    
    return [board, game];
}

//==============================================================================

//===== AI API CALLS ===========================================================
//In this section, we create API calls to both versions of Stockfish to predict the 
//next best move for a given FEN.
//The keywords “async” and “await” are used to pause the code until the function 
//call is completed.
//I've used an example of how to use these keywords here: 
//https://javascript.info/async-await
//==============================================================================

function generatePromotionMove(fen) {
    const chess = new Chess(fen);
    const moves = chess.moves({ verbose: true });

    const promotionMove = moves.find(move => move.promotion && move.promotion === 'q');
    if (promotionMove) {
        return promotionMove.san;
    }
    return null;
}


async function getBestMoveStockfishv1(fen, depth=13) {
    const apiurl = "https://stockfish.online/api/stockfish.php";

    const params = new URLSearchParams({
        fen: fen,
        depth: depth,
        mode: "bestmove"
    });

    try {
        const response = await fetch(`${apiurl}?${params.toString()}`, {
            method: 'GET',
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            }
        });

        if (!response.ok) {
            throw new Error(`API Error: ${response.status} - ${response.statusText}`);
        }

        const json = await response.json();
        const text = json.data;
        
        const bestMove = text.split(' ')[1];
    
        const chess = new Chess(fen);
        chess.move({ from: bestMove.slice(0, 2), to: bestMove.slice(2, 4) });

        const sanMove = chess.history({ verbose: true })[0].san;
        return sanMove;
        
        
    }
    catch (error) {
        //if there is an error, we check if we can make a promotion move
        const promotionMove = generatePromotionMove(fen);
            if (promotionMove) {
                return promotionMove;
            }
        
        if (game.mode == 'menu') { document.getElementById("text-bar").textContent = "An error has occurred during the API call...try to restart the game!"; }
        if (game.mode == 'puzzle') { document.getElementById("text-bar").textContent = "An error has occurred during the API call...try to load a new puzzle!"; }
        if (game.mode == 'analysis') { document.getElementById("text-bar").textContent = "An error has occurred during the API call...try to restart the game!"; }
        
    }
}


async function getBestMoveStockfishv2(fen, depth=13) {
    const apiurl = "https://stockfish.online/api/s/v2.php";

    const params = new URLSearchParams({
        fen: fen,
        depth: depth,
    });

    try {
        const response = await fetch(`${apiurl}?${params.toString()}`, {
            method: 'GET',
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            }
        });

        if (!response.ok) {
            throw new Error(`API Error: ${response.status} - ${response.statusText}`);
        }

        const json = await response.json();
        
        const bestMove = json.bestmove.split(' ')[1];
        
        const chess = new Chess(fen);
        chess.move({ from: bestMove.slice(0, 2), to: bestMove.slice(2, 4) });

        const sanMove = chess.history({ verbose: true })[0].san;
        return sanMove;
        
    }
    catch (error) {
        //if there is an error, we check if we can make a promotion move
        const promotionMove = generatePromotionMove(fen);
        if (promotionMove) {
            return promotionMove;
        }
        
        if (game.mode == 'menu') { document.getElementById("text-bar").textContent = "An error has occurred during the API call...try to restart the game!"; }
        if (game.mode == 'puzzle') { document.getElementById("text-bar").textContent = "An error has occurred during the API call...try to load a new puzzle!"; }
        if (game.mode == 'analysis') { document.getElementById("text-bar").textContent = "An error has occurred during the API call...try to restart the game!"; }

    }
}

//==============================================================================

//===== MENU ===================================================================
//In the menu, you can play a game against the AI, use buttons in the 
//information panel to restart the game, browse through old moves...
//Buttons can also be used to access mini-games.
//The main functions in this section are :
//- initMenu(), which initializes the board and information panel buttons
//- moveMenu(), which is activated when a chessboard piece is dropped at a valid 
//position (onDrop function). When the user has played, the function calls the 
//v2 API to determine the best move.
//The other functions in this section are simply tools called in these 2 
//previous methods.
//==============================================================================

function pause(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

function testGameOver(game) {
    if (game.game_over()) {
        if (game.in_draw()) {
            document.getElementById("text-menu").textContent = "The game is a draw...";
            document.getElementById("text-bar").textContent = "The game is a draw...";
        }
        else {
            if (game.turn() == 'b') {
                document.getElementById("text-menu").textContent = "Checkmate! White has won the game...";
                document.getElementById("text-bar").textContent = "Checkmate! White has won the game...";

            }
            else if (game.turn() == 'w') {
                document.getElementById("text-menu").textContent = "Checkmate! Black has won the game...";
                document.getElementById("text-bar").textContent = "Checkmate! Black has won the game...";
            }
        }
    }
}


function initMenu(board, game) {
    
    document.getElementById("text-bar").textContent = "Waiting for user action...";
    
    board.currentIndexMenu = 0;
    board.arrowIndexMenu = 0;
    board.sequenceMenu = [];
    game.mode = 'menu';
    
    $('#restart-menu').off('click').on('click', function() {
        [board, game] = initBoard('chess-board');
        initMenu(board, game);
        document.getElementById("text-menu").textContent = "The game hasn't started yet...";
        document.getElementById("text-bar").textContent = "Waiting for user action...";
    });
    
    $('#orientation-menu').off('click').on('click', async function() {
        board.flip();
        if ((board.orientation() == 'white' && game.turn() == 'b') || (board.orientation() == 'black' && game.turn() == 'w')) {
            
            game.mode = 'navigation';
            document.getElementById("text-bar").textContent = "The game is on... (AI turn)";
            
            if (game.fen() == 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1') {
                game.mode = 'navigation';
                document.getElementById("text-menu").textContent = "The game is on...";
                document.getElementById("text-bar").textContent = "The game is on... (AI turn)";
            }
            const stockfishMove = await getBestMoveStockfishv2(game.fen());
            game.move(stockfishMove);
            board.position(game.fen());
            (board.sequenceMenu).push(stockfishMove);
            board.currentIndexMenu++;
            board.arrowIndexMenu++;
            
            game.mode = 'menu';
            document.getElementById("text-bar").textContent = "The game is on... (User turn)";
            
            testGameOver(game);
        }
    });
    
    function updateMenuPosition() {
        game.load('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
        for (let i = 0; i < board.arrowIndexMenu; i++) {
            game.move(board.sequenceMenu[i]);
        }
        board.position(game.fen());
    }

    $('#start-menu').off('click').on('click', function() {
        board.arrowIndexMenu = 0;
        updateMenuPosition();
    });

    $('#pre-move-menu').off('click').on('click', function() {
        if (board.currentIndexMenu > 0 && board.arrowIndexMenu > 0) {
            board.arrowIndexMenu--;
            updateMenuPosition();
        }
    });

    $('#next-move-menu').off('click').on('click', function() {
        if (board.arrowIndexMenu < board.currentIndexMenu)  {
            board.arrowIndexMenu++;
            updateMenuPosition();
        }
    });

    $('#current-menu').off('click').on('click', function() {
        board.arrowIndexMenu = board.currentIndexMenu;
        updateMenuPosition();
    });
}

async function moveMenu(source, target, board, name, game) {
    
    removeGreySquares(name);
    
    const userMove = game.move({
        from: source,
        to: target,
        promotion: 'q'
    });
    
    if (userMove === null) return 'snapback';
    else {
        
        (board.sequenceMenu).push(userMove.san);
        board.currentIndexMenu++;
        board.arrowIndexMenu++;
        
        game.mode = 'navigation';
        document.getElementById("text-bar").textContent = "The game is on... (AI turn)";
        
        if ((board.sequenceMenu).length > 0) {
            textMenu = document.getElementById("text-menu");
            if (textMenu.textContent !== "The game is on...") {
                textMenu.textContent = "The game is on...";
                game.mode = 'navigation';
                document.getElementById("text-bar").textContent = "The game is on... (AI turn)";
            }
        }
        
        testGameOver(game);
        
        const stockfishMove = await getBestMoveStockfishv2(game.fen());
        game.move(stockfishMove);
        board.position(game.fen());
        (board.sequenceMenu).push(stockfishMove);
        board.currentIndexMenu++;
        board.arrowIndexMenu++;
        
        game.mode = 'menu';
        document.getElementById("text-bar").textContent = "The game is on... (User turn)";
        
        testGameOver(game);
    }
}

//==============================================================================

//===== PUZZLE GAME ============================================================
//In the Puzzle Game, the loadPuzzle() function uses the chess.com API to access 
//random puzzles. For each puzzle, we extract the FEN (to obtain the starting 
//position) and the PGN (to obtain the sequence of moves to play to complete 
//the puzzle).
//We then use initPuzzle() to create the chessboard, import the position and 
//activate all the panel buttons. In particular, we activate the “Solution” 
//and “Next Move” buttons to visualize on the chessboard the moves the user 
//should have made to advance in the puzzle.
//We then call these 2 methods in a third: startPuzzle() and add a movePuzzle() 
//function, which is activated again when a piece is validly moved on the 
//chessboard. If the puzzle is completed (or the “Next Puzzle” button pressed), 
//we call the StartPuzzle() method again to obtain a new puzzle and create a loop.

//The functions at the beginning of this section are used to calculate the 
//accuracy values which are then displayed on the panel.
//I should also point out that puzzle PGNs vary considerably and I've tried to 
//create a robust extraction method as you can see in loadPuzzle(), 
//but some PGNs still escape the algorithm and in this case it's necessary 
//to move on to the next puzzle with the button.
//==============================================================================

let userScore = 0;

let userNumerator = 0;
let userDenominator = 0;
let userAccuracy = 0;

function userUpdateAccuracy(value) {
    userDenominator++;
    if (value) {
        userNumerator++;
    }

    userAccuracy = Math.round((userNumerator / userDenominator) * 100);
}

let stockfishv1Numerator = 0;
let stockfishv1Denominator = 0;
let stockfishv2Numerator = 0;
let stockfishv2Denominator = 0;
let stockfishv1Accuracy = 0;
let stockfishv2Accuracy = 0;

function stockfishv1UpdateAccuracy(value) {
    stockfishv1Denominator++;
    if (value) {
        stockfishv1Numerator++;
    }

    stockfishv1Accuracy = Math.round((stockfishv1Numerator / stockfishv1Denominator) * 100);
}

function stockfishv2UpdateAccuracy(value) {
    stockfishv2Denominator++;
    if (value) {
        stockfishv2Numerator++;
    }

    stockfishv2Accuracy = Math.round((stockfishv2Numerator / stockfishv2Denominator) * 100);
}

async function stockfishUpdate(board, game) {
    
    // Stockfish v1
    const stockfishv1Move = await getBestMoveStockfishv1(game.fen());
    
    if (stockfishv1Move === board.sequencePuzzle[board.currentIndexPuzzle]) {
        stockfishv1UpdateAccuracy(true);
    }
    else {
        if (stockfishv1Move) { stockfishv1UpdateAccuracy(false); }
    }
    
    // Stockfish v2
    const stockfishv2Move = await getBestMoveStockfishv2(game.fen());
    
    if (stockfishv2Move === board.sequencePuzzle[board.currentIndexPuzzle]) {
        stockfishv2UpdateAccuracy(true);
    }
    else {
        if (stockfishv2Move) { stockfishv2UpdateAccuracy(false); }
    }
}


let currentStartFEN;

async function loadPuzzle() {
    
    document.getElementById("text-bar").textContent = "Loading puzzle...";
    let startFEN;
    let data;
    
    do {
        const response = await fetch("https://api.chess.com/pub/puzzle/random");        
        
        if (!response.ok) {
            throw new Error(`Error while retrieving the puzzle: ${response.statusText}`);
        }
        
        data = await response.json();
        
        if (!data || !data.fen || !data.pgn) {
            throw new Error("Invalid or incomplete puzzle data...");
        }
        
        startFEN = data.fen;

    } while (startFEN === currentStartFEN);
    
    currentStartFEN = startFEN;

    const chess = new Chess();

    const loaded = chess.load(startFEN);
    if (!loaded) {
        console.error("Error loading position with FEN...");
        return;
    }

    let pgn = data.pgn;

    pgn = pgn.substring(pgn.lastIndexOf(']') + 1);
    pgn = pgn.replace(/\{[^}]*\}/g, '');
    pgn = pgn.replace(/\([^)]*\}/g, '');
    pgn = pgn.split("\n").join(" ");
    
    const moveSequence = pgn
        .replace(/\d+\./g, '')
        .replace(/\.\./g, '')
        .split(/\s+/)
        .map(move => move.replace(/\r/g, ''))
        .filter(move => move !== "" && move !== "*");
    

    moveSequence.forEach(move => chess.move(move));
    const endFEN = chess.fen();
    document.getElementById("text-bar").textContent = "Puzzle loaded!";
    
    return [startFEN, endFEN, moveSequence];
    
}

function initPuzzle(board, game, startFEN, moveSequence) {
    
    game.load(startFEN);
    board.position(game.fen());
    
    board.currentIndexPuzzle = 0;
    board.arrowIndexPuzzle = 0;
    board.sequencePuzzle = moveSequence;
    game.endPuzzle = false;
    
    stockfishUpdate(board, game);

    
    $('#back-button-puzzle').off('click').on('click', function() {
        $('#info-panel-puzzle').css('display', 'none');
        [board, game] = initBoard('chess-board');
        initMenu(board, game);
    });
    
    
    $('#solution-button').off('click').on('click', async function() {
        
        $('#solution-button').prop('disabled', true);
        $('#next-move-button').prop('disabled', true);
        $('#next-puzzle-button').prop('disabled', true);
        
        if (!(game.endPuzzle)) {
            document.getElementById("text-bar").textContent = "Explanation of the solution...";
            userUpdateAccuracy(false);
            document.getElementById("user-accuracy").textContent = userAccuracy + "%";
            document.getElementById("stockfishv1-accuracy").textContent = stockfishv1Accuracy + "%";
            document.getElementById("stockfishv2-accuracy").textContent = stockfishv2Accuracy + "%";
        }
        
        for (let i=board.currentIndexPuzzle; i<board.sequencePuzzle.length; i++) {
            game.move(board.sequencePuzzle[board.currentIndexPuzzle]);
            board.position(game.fen());
            board.currentIndexPuzzle++;
            board.arrowIndexPuzzle++;
            await pause(500);
        }
        
        game.endPuzzle = true;
        document.getElementById("text-bar").textContent = "Puzzle completed!";
        
        $('#solution-button').prop('disabled', false);
        $('#next-move-button').prop('disabled', false);
        $('#next-puzzle-button').prop('disabled', false);
    });
    
    $('#next-move-button').off('click').on('click', async function() {
        
        $('#solution-button').prop('disabled', true);
        $('#next-move-button').prop('disabled', true);
        $('#next-puzzle-button').prop('disabled', true);

        if (!(game.endPuzzle)) {
            
            document.getElementById("text-bar").textContent = "Explanation of next move...";
            userUpdateAccuracy(false);
            document.getElementById("user-accuracy").textContent = userAccuracy + "%";
            document.getElementById("stockfishv1-accuracy").textContent = stockfishv1Accuracy + "%";
            document.getElementById("stockfishv2-accuracy").textContent = stockfishv2Accuracy + "%";
            
            for(let i=0; i<2; i++) {
                game.move(board.sequencePuzzle[board.currentIndexPuzzle]);
                board.position(game.fen());
                board.currentIndexPuzzle++;
                board.arrowIndexPuzzle++;
                
                if (board.currentIndexPuzzle === board.sequencePuzzle.length) {
                    game.endPuzzle = true;
                    document.getElementById("text-bar").textContent = "Puzzle completed!";
                }
                await pause(500);
            }
            
            if (!game.endPuzzle) {
                document.getElementById("text-bar").textContent = "User turn now!";
                stockfishUpdate(board, game);
            }
        }
        
        $('#solution-button').prop('disabled', false);
        $('#next-move-button').prop('disabled', false);
        $('#next-puzzle-button').prop('disabled', false);
    })
    
    $('#next-puzzle-button').off('click').on('click', function() {
        
        $('#solution-button').prop('disabled', true);
        $('#next-move-button').prop('disabled', true);
        $('#next-puzzle-button').prop('disabled', true);
        
        if (!(game.endPuzzle)) {
            userUpdateAccuracy(false);
            document.getElementById("user-accuracy").textContent = userAccuracy + "%";
            document.getElementById("stockfishv1-accuracy").textContent = stockfishv1Accuracy + "%";
            document.getElementById("stockfishv2-accuracy").textContent = stockfishv2Accuracy + "%";
            game.endPuzzle = true;
        }
        startPuzzle();
        
        $('#solution-button').prop('disabled', false);
        $('#next-move-button').prop('disabled', false);
        $('#next-puzzle-button').prop('disabled', false);
    });
    

    function updatePuzzlePosition() {
        game.load(startFEN);
        for (let i = 0; i < board.arrowIndexPuzzle; i++) {
            game.move(board.sequencePuzzle[i]);
        }
        board.position(game.fen());
    }

    $('#start-puzzle').off('click').on('click', function() {
        game.mode = 'navigation';
        board.arrowIndexPuzzle = 0;
        updatePuzzlePosition();
    });

    $('#pre-move-puzzle').off('click').on('click', function() {
        if (board.currentIndexPuzzle > 0 && board.arrowIndexPuzzle > 0) {
            game.mode = 'navigation';
            board.arrowIndexPuzzle--;
            updatePuzzlePosition();
        }
    });

    $('#next-move-puzzle').off('click').on('click', function() {
        if (board.arrowIndexPuzzle < board.currentIndexPuzzle)  {
            game.mode = 'navigation';
            board.arrowIndexPuzzle++;
            if (board.arrowIndexPuzzle == board.currentIndexPuzzle) { game.mode = 'puzzle'; }
            updatePuzzlePosition();
        }
    });

    $('#current-puzzle').off('click').on('click', function() {
        game.mode = 'puzzle';
        board.arrowIndexPuzzle = board.currentIndexPuzzle;
        updatePuzzlePosition();
    });
    
    return [game, board.currentIndexPuzzle];
        
}



let replay = false;

function movePuzzle(source, target, board, name, game) {
    
    removeGreySquares(name);
    
    const userMove = game.move({
        from: source,
        to: target,
        promotion: 'q'
    });
    
    if (!userMove) {
        return 'snapback';
    }
    else if (userMove.san === board.sequencePuzzle[board.currentIndexPuzzle]) {
        
        document.getElementById("text-bar").textContent = "Well done! What's next?";
        
        if (!replay) { userUpdateAccuracy(true); }
        replay = false;
        document.getElementById("user-accuracy").textContent = userAccuracy + "%";
        document.getElementById("stockfishv1-accuracy").textContent = stockfishv1Accuracy + "%";
        document.getElementById("stockfishv2-accuracy").textContent = stockfishv2Accuracy + "%";
        
        board.currentIndexPuzzle++;
        board.arrowIndexPuzzle++;
        board.position(game.fen());

        
        if (board.currentIndexPuzzle === board.sequencePuzzle.length) {
            userScore++;
            document.getElementById("score").textContent = userScore;
            game.endPuzzle = true;
            document.getElementById("text-bar").textContent = "Puzzle completed!";
            startPuzzle();
            return 0;
        }
        else {
            game.move(board.sequencePuzzle[board.currentIndexPuzzle]);
            board.position(game.fen());
            board.currentIndexPuzzle++;
            board.arrowIndexPuzzle++;
        }
    } 
    else {
        
        if (!replay) {
            userUpdateAccuracy(false);
            replay = true;
            
            document.getElementById("user-accuracy").textContent = userAccuracy + "%";
            document.getElementById("stockfishv1-accuracy").textContent = stockfishv1Accuracy + "%";
            document.getElementById("stockfishv2-accuracy").textContent = stockfishv2Accuracy + "%";
        }
        document.getElementById("text-bar").textContent = "It's not the right move! Here's a hint..." + board.sequencePuzzle[board.currentIndexPuzzle];
        game.undo();
        board.position(game.fen());
        return 'snapback';
    }
    
    stockfishUpdate(board, game);
}


async function startPuzzle(board, game) {
    
    [board, game] = initBoard('chess-board');
    game.mode = 'puzzle';
    document.getElementById("text-bar").textContent = "Puzzles game!";
    
    [startFEN, endFEN, moveSequence] = await loadPuzzle();
    [game, currentIndexPuzzle] = initPuzzle(board, game, startFEN, moveSequence);
}

//==============================================================================

//===== ANALYSIS GAME ==========================================================
//The Analyse Game structure is similar to the 2 previous structures. 
//An initAnalysis() function imports the board, allows settings to be modified 
//and activates buttons.
//Then, a loopAnalysis() function advances the game if you're in “play” mode, 
//and stops it if you're in “pause” mode.
//There is no move function, as the pieces cannot be moved, and the two previous 
//functions are called again in a startAnalysis() function. This function is 
//then called in several places, notably to restart the game.
//There's also the generateRandomFEN() tool for generating valid, 
//random FEN positions. This allows you to diversify the mini-game!
//==============================================================================

function generateRandomFEN() {
    const chess = new Chess();

    const movesCount = Math.floor(Math.random() * 50) + 1;
    for (let i = 0; i < movesCount; i++) {
        const possibleMoves = chess.moves();
        if (possibleMoves.length === 0) {
            break;
        }
        const randomMove = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
        chess.move(randomMove);
    }

    return chess.fen();
}

let analysisFEN;

function initAnalysis(board, game) {
    
    document.getElementById("text-bar").textContent = "Analysis game! You can change the settings before starting the game by pressing the START button!";
    
    board.currentIndexAnalysis = 0;
    board.arrowIndexAnalysis = 0;
    board.sequenceAnalysis = [];
    game.playAnalysis = false;
    analysisFEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
    
    $('#back-button-analysis').off('click').on('click', function() {
        $('#info-panel-analysis').css('display', 'none');
        [board, game] = initBoard('chess-board');
        initMenu(board, game);
    });
    
    $('#white').on('change', function() {
        const whiteChoice = $(this).val();
        const blackSelect = $('#black');
        blackSelect.empty();
        
        if (whiteChoice === "v1") {
            blackSelect.append('<option value="v2">Stockfish v2</option>');
        } else if (whiteChoice === "v2") {
            blackSelect.append('<option value="v1">Stockfish v1</option>');
        }
    });
    
    $('#black').on('change', function() {
        const blackChoice = $(this).val();
        const whiteSelect = $('#white');
        whiteSelect.empty();
        
        if (blackChoice === "v1") {
            whiteSelect.append('<option value="v2">Stockfish v2</option>');
        } else if (blackChoice === "v2") {
            whiteSelect.append('<option value="v1">Stockfish v1</option>');
        }
    });
    
    
    $('#play-analysis').off('click').on('click', function() {
        
        document.getElementById("text-bar").textContent = "The game is in progress...";
        board.currentIndexAnalysis = board.arrowIndexAnalysis;
        board.sequenceAnalysis = (board.sequenceAnalysis).slice(0, board.currentIndexAnalysis);
        game.playAnalysis = true;
        
        $('#start-analysis').prop('disabled', true);
        $('#pre-move-analysis').prop('disabled', true);
        $('#next-move-analysis').prop('disabled', true);
        $('#current-analysis').prop('disabled', true);

        loopAnalysis(board, game);
    });
    
    $('#pause-analysis').off('click').on('click', function() {
        
        document.getElementById("text-bar").textContent = "The game is currently paused...";
        $('#start-analysis').prop('disabled', false);
        $('#pre-move-analysis').prop('disabled', false);
        $('#next-move-analysis').prop('disabled', false);
        $('#current-analysis').prop('disabled', false);
        
        game.playAnalysis = false;
    });
    
    $('#restart-analysis').off('click').on('click', function() {
        startAnalysis(board, game);
    });
    
    $('#orientation-analysis').off('click').on('click', function() {
        board.flip();
    });
    
    $('#random-analysis').off('click').on('click', function() {
        game.playAnalysis = false;
        board.currentIndexAnalysis = 0;
        board.arrowIndexAnalysis = 0;
        board.sequenceAnalysis = [];
        document.getElementById("text-bar").textContent = "The random valid position has been loaded...press START!";
        analysisFEN = generateRandomFEN();
        game.load(analysisFEN);
        board.position(game.fen());
    });
    
    function updateAnalysisPosition() {
        game.load(analysisFEN);
        for (let i = 0; i < board.arrowIndexAnalysis; i++) {
            game.move(board.sequenceAnalysis[i]);
        }
        board.position(game.fen());
    }

    $('#start-analysis').off('click').on('click', function() {
        board.arrowIndexAnalysis = 0;
        updateAnalysisPosition();
    });

    $('#pre-move-analysis').off('click').on('click', function() {
        if (board.currentIndexAnalysis > 0 && board.arrowIndexAnalysis > 0) {
            board.arrowIndexAnalysis--;
            updateAnalysisPosition();
        }
    });

    $('#next-move-analysis').off('click').on('click', function() {
        if (board.arrowIndexAnalysis < board.currentIndexAnalysis)  {
            board.arrowIndexAnalysis++;
            updateAnalysisPosition();
        }
    });

    $('#current-analysis').off('click').on('click', function() {
        board.arrowIndexAnalysis = board.currentIndexAnalysis;
        updateAnalysisPosition();
    });
}


async function loopAnalysis(board, game) {
    
    depth = Number(document.getElementById('depth').value);

    speed = document.getElementById('speed').value;
    if (speed == 'fast') { speed = 0; }
    else if (speed == 'medium') { speed = 3; }
    else if (speed == 'slow') { speed = 6; }
    
    
    while (game.mode == 'analysis' && !game.game_over() && game.playAnalysis) {
        
        if (document.getElementById('white').value == "v1" && document.getElementById('black').value == "v2") {
            if (game.turn() == 'w') {
                await pause(speed*500);
            
                if (game.playAnalysis) {
                    const whiteMove = await getBestMoveStockfishv1(game.fen(), depth);
                    game.move(whiteMove);
                    await board.position(game.fen());
                    (board.sequenceAnalysis).push(whiteMove);
                    board.currentIndexAnalysis++;
                    board.arrowIndexAnalysis++;
                }
            }
            
            else if (game.turn() == 'b') {
                await pause(speed*500);
                
                if (game.playAnalysis) {
                    const blackMove = await getBestMoveStockfishv2(game.fen(), depth);
                    game.move(blackMove);
                    await board.position(game.fen());
                    (board.sequenceAnalysis).push(blackMove);
                    board.currentIndexAnalysis++;
                    board.arrowIndexAnalysis++;
                }
            }
        }
        else if (document.getElementById('white').value == "v2" && document.getElementById('black').value == "v1") {
            if (game.turn() == 'w') {
                await pause(speed*500);
            
                if (game.playAnalysis) {
                    const whiteMove = await getBestMoveStockfishv2(game.fen(), depth);
                    game.move(whiteMove);
                    await board.position(game.fen());
                    (board.sequenceAnalysis).push(whiteMove);
                    board.currentIndexAnalysis++;
                    board.arrowIndexAnalysis++;
                }
            }
            
            else if (game.turn() == 'b') {
                await pause(speed*500);
                
                if (game.playAnalysis) {
                    const blackMove = await getBestMoveStockfishv1(game.fen(), depth);
                    game.move(blackMove);
                    await board.position(game.fen());
                    (board.sequenceAnalysis).push(blackMove);
                    board.currentIndexAnalysis++;
                    board.arrowIndexAnalysis++;
                }
            }
        }
    }
    
    if (game.game_over()) {
        if (game.in_draw()) {
            document.getElementById("text-bar").textContent = "The game is a draw...";
        }
        else {
            if (game.turn() == 'b') {
                document.getElementById("text-bar").textContent = "Checkmate! White has won the game...";
            }
            else if (game.turn() == 'w') {
                document.getElementById("text-bar").textContent = "Checkmate! Black has won the game...";
            }
        }
    }
}

function startAnalysis(board, game) {
    
    [board, game] = initBoard('chess-board');
    game.mode = 'analysis';
    
    initAnalysis(board, game);

}

//==============================================================================

//===== MAIN ===================================================================

$(document).ready(function() {

//===== CSS PART ===============================================================
//The CSS part was the hardest for me to code, as I was a total beginner in html 
//and css.
//I relied entirely on the following documentation to create my wrappers and divs.
//This one in particular for the positions of the elements in my divs: 
//https://developer.mozilla.org/en-US/docs/Web/CSS/position
//This one in particular for DOM Document/Elements: 
//https://www.w3schools.com/js/js_htmldom.asp
//Finally, I found a lot of inspiration in the other Ancient-Brain projects 
//for the creation of buttons and other interactive elements.
//==============================================================================

    $('<link/>', {
        rel: 'stylesheet',
        type: 'text/css',
        href: '/uploads/korentin/chessboard.css'
    }).appendTo('head');
    
    $('<style>')
    .prop('type', 'text/css')
    .html(`
        body {
            background-color: #d1b28f !important;
            margin: 0;
            padding: 0;
        }
    `).appendTo('head');
    
    $('#ab-wrapper').append('<div id="main-wrapper" style="display: flex; flex-direction: column; align-items: flex-start; position: absolute; z-index: 20; top: 45%; left: 50%; transform: translate(-50%, -50%);"></div>');
    
    $('#main-wrapper').append('<div id="top-wrapper" style="display: flex; justify-content: center;"></div>');

    
    //===== CHESSBOARD =========================================================
    
    $('#top-wrapper').append('<div id="chess-wrapper" style="width: 402px; margin: 20px 10px;"></div>');

    $('#chess-wrapper').append('<div id="chess-board" style="width:100%; margin:0 auto;"></div>');
    
    //==========================================================================
    
    //===== INFO PANEL =========================================================
    
    $('#top-wrapper').append('<div id="info-panel" style="width: 402px; height: 402px; background-color: #f0d9b5; color: black; margin: 20px 10px; padding: 20px; box-sizing: border-box; overflow: hidden; position: relative; display: block; flex-direction: column; border: 2px solid #404040;"></div>');

    // Title
    $('#info-panel').append(`
        <div class="panel-header" style="background-color: #b58863; color: white; padding: 15px; display: flex; justify-content: space-between; align-items: center; width: calc(100% - (2 * 15px)); position: absolute; top: 0; left: 0;">
            <h2 style="margin: 0; font-size: 1.5em; flex-grow: 1; text-align: center;">CHESS API GAME</h2>
        </div>
    `);
    
    // Mini-Games Section
    $('#info-panel').append(`
        <div class="mini-game-section" style="display: flex; flex-direction: column; align-items: center; margin-top: 50;">
            <h2 style="margin: 0; font-size: 1.2em; text-align: center; color: #404040;">MINI-GAMES</h2>
            <div style="display: flex; justify-content: space-between; width: 100%; margin-top: 15px;">
                <button id="puzzle-button" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #404040; cursor: pointer; border-radius: 5px; border: 2px solid #404040;">Puzzles</button>
                <button id="analysis-button" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #404040; cursor: pointer; border-radius: 5px; border: 2px solid #404040;">Analysis</button>
            </div>
            <hr style="width: 80%; border: 1px solid #404040; margin-top: 20px;"/>
        </div>
    `);
    
    // Computer Game Section
    $('#info-panel').append(`
        <div class="computer-game-section" style="display: flex; flex-direction: column; text-align: left; margin-top: 5px;">
            <h2 style="margin: 0; font-size: 1.2em; text-align: center; color: #404040;">COMPUTER GAME</h2>
            <div style="display: flex; justify-content: space-between; width: 100%; margin-top: 15px;">
                <button id="restart-menu" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #b58863; cursor: pointer; border-radius: 5px; border: 2px solid #b58863;">Restart</button>
                <button id="orientation-menu" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #b58863; cursor: pointer; border-radius: 5px; border: 2px solid #b58863;">Orientation</button>
            </div>
            <div style="margin-top: 16px; padding: 10px; border: 2px solid #404040; border-radius: 5px; background-color: white; color: #404040;">
                <span id="text-menu" style="font-weight: normal;">The game hasn't started yet...</span>
            </div>
        </div>
    `);

    
    // Navigation
    $('#info-panel').append(`
        <div class="navigation-section" style="background-color: #b58863; display: flex; justify-content: space-around; padding: 10px; position: absolute; bottom: 20px; left: 20px; right: 20px; border-radius: 5px;">
            <button id="start-menu" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">⇐</button>
            <button id="pre-move-menu" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">←</button>
            <button id="next-move-menu" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">→</button>
            <button id="current-menu" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">⇒</button>
        </div>
    `);
    
    //==========================================================================
    
    //===== INFORMATION BAR ====================================================
    
    $('#main-wrapper').append('<div id="info-bar" style="width: calc(100% - 20px); height: 40px; background-color: white; margin: 0 10px; padding: 5px; box-sizing: border-box; overflow: hidden; position: relative; display: block; flex-direction: column; border: 2px solid #404040;"></div>');
        
    $('#info-bar').append(`
        <div style="width: 100%; background-color: white; color: #404040; display: flex; align-items: center; height: 100%;">
            <span id="text-bar" style="font-weight: normal; padding: 0 5px">Waiting for user action...</span>
        </div>
    `);
    
    //==========================================================================
    
    //===== INFO PANEL PUZZLE ==================================================
    
    $('#info-panel').append('<div id="info-panel-puzzle" style="width: 100%; height: 100%; background-color: #f0d9b5; padding: 20px; box-sizing: border-box; overflow: hidden; position: absolute; top: 0; left: 0; display: none;"></div>');
    
    // Title
    $('#info-panel-puzzle').append(`
        <div class="panel-header" style="background-color: #b58863; color: white; padding: 15px; display: flex; flex-direction: column; justify-content: space-between; align-items: center; width: calc(100% - (2 * 15px)); position: absolute; top: 0; left: 0;">
            <button id="back-button-puzzle" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em; position: absolute; left:20px; top:50%; transform:translateY(-50%);">←</button>
            <h2 style="margin: 0; font-size: 1.5em; flex-grow: 1; text-align: center;">PUZZLES</h2>
        </div>
    `);

    // Score & Accuracy
    $('#info-panel-puzzle').append(`
        <div class="score-section" style="margin-top: 50px; font-size: 1.2em; display: flex; flex-direction: column; align-items: center;">
            <p style="margin: 5px 0; color: #404040; font-weight: bold;">SCORE : <span id="score" style="font-weight: bold;">0</span></p>
            <p style="margin: 5px 0; color: #404040;">My accuracy : <span id="user-accuracy" style="font-weight: bold;">0%</span></p>
            <p style="margin: 5px 0; color: #404040;">Stockfish v1 API accuracy : <span id="stockfishv1-accuracy" style="font-weight: bold;">0%</span></p>
            <p style="margin: 5px 0; color: #404040;">Stockfish v2 API accuracy : <span id="stockfishv2-accuracy" style="font-weight: bold;">0%</span></p>
        </div>
    `);

    // Solution Section
    $('#info-panel-puzzle').append(`
        <div class="solution-section" style="display: flex; justify-content: space-between; margin-top: 15;">
            <button id="solution-button" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #404040; cursor: pointer; border-radius: 5px; border: 2px solid #404040;">Solution</button>
            <button id="next-move-button" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #404040; cursor: pointer; border-radius: 5px; border: 2px solid #404040;">Next Move</button>
        </div>
    `);
    
    // Next Puzzle
    $('#info-panel-puzzle').append(`
        <div class="next-section" style="display: flex; justify-content: center; align-items: center; margin-top: 20px;">
            <button id="next-puzzle-button" style="width: 45%; padding: 8px; font-size: 1em; color: white; background-color: #404040; cursor: pointer; border-radius: 5px; border: 2px solid #404040;">Next Puzzle</button>
        </div>
    `);

    // Navigation
    $('#info-panel-puzzle').append(`
        <div class="navigation-section" style="background-color: #b58863; display: flex; justify-content: space-around; padding: 10px; position: absolute; bottom: 20px; left: 20px; right: 20px; border-radius: 5px;">
            <button id="start-puzzle" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">⇐</button>
            <button id="pre-move-puzzle" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">←</button>
            <button id="next-move-puzzle" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">→</button>
            <button id="current-puzzle" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">⇒</button>
        </div>
    `);
    
    //==========================================================================
    
    //===== INFO PANEL ANALYSIS ================================================
    
    $('#info-panel').append('<div id="info-panel-analysis" style="width: 100%; height: 100%; background-color: #f0d9b5; padding: 20px; box-sizing: border-box; overflow: hidden; position: absolute; top: 0; left: 0; display: none;"></div>');
    
    // Title
    $('#info-panel-analysis').append(`
        <div class="panel-header" style="background-color: #b58863; color: white; padding: 15px; display: flex; flex-direction: column; justify-content: space-between; align-items: center; width: calc(100% - (2 * 15px)); position: absolute; top: 0; left: 0;">
            <button id="back-button-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em; position: absolute; left:20px; top:50%; transform:translateY(-50%);">←</button>
            <h2 style="margin: 0; font-size: 1.5em; flex-grow: 1; text-align: center;">ANALYSIS</h2>
        </div>
    `);
    
    // Parameters Section
    $('#info-panel-analysis').append(`
        <div class="parameters-section" style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 50px;  padding: 0 20px;">
            <h2 style="margin: 0; font-size: 1.2em; text-align: center; color: #404040;">SETTINGS</h2>
            
            <div style="display: flex; flex-direction: column; align-items: flex-start; width: 100%; margin-top: 10px;">
            
            <!-- White Selection -->
            <div style="display: flex; justify-content: space-between; margin-bottom: 10px; text-align: left; width: 100%;">
                <label for="white" style="flex: 1; font-size: 1em; color: #404040;">White:</label>
                <select id="white" style="flex: 2; padding: 5px; color: #404040;">
                    <option value="v1" style="color: #404040;">Stockfish v1</option>
                    <option value="v2" style="color: #404040;">Stockfish v2</option>
                </select>
            </div>

            <!-- Black Selection -->
            <div style="display: flex; justify-content: space-between; margin-bottom: 10px; text-align: left; width: 100%;">
                <label for="black" style="flex: 1; font-size: 1em; color: #404040;">Black:</label>
                <select id="black" style="flex: 2; padding: 5px; color: #404040;">
                    <option value="v2" style="color: #404040;">Stockfish v2</option>
                    <option value="v1" style="color: #404040;">Stockfish v1</option>
                </select>
            </div>
            
            <!-- Depth Selection -->
            <div style="display: flex; justify-content: space-between; margin-bottom: 10px; text-align: left; width: 100%;">
                <label for="depth" style="flex: 1; font-size: 1em; color: #404040;">Depth:</label>
                <select id="depth" style="flex: 2; padding: 5px; color: #404040;">
                    <option value="1" style="color: #404040;">1</option>
                    <option value="2" style="color: #404040;">2</option>
                    <option value="3" style="color: #404040;">3</option>
                    <option value="4" style="color: #404040;">4</option>
                    <option value="5" style="color: #404040;">5</option>
                    <option value="6" style="color: #404040;">6</option>
                    <option value="7" style="color: #404040;">7</option>
                    <option value="8" style="color: #404040;">8</option>
                    <option value="9" style="color: #404040;">9</option>
                    <option value="10" style="color: #404040;">10</option>
                    <option value="11" style="color: #404040;">11</option>
                    <option value="12" style="color: #404040;">12</option>
                    <option value="13" style="color: #404040;">13</option>
                </select>
            </div>

            <!-- Speed Selection -->
            <div style="display: flex; justify-content: space-between; margin-bottom: 10px; text-align: left; width: 100%;">
                <label for="speed" style="flex: 1; font-size: 1em; color: #404040;">Speed:</label>
                <select id="speed" style="flex: 2; padding: 5px; color: #404040;">
                    <option value="fast" style="color: #404040;">Fast</option>
                    <option value="medium" style="color: #404040;">Medium</option>
                    <option value="slow" style="color: #404040;">Slow</option>
                </select>
            </div>
        </div>
    `);
    
    //Game Parameters
    $('#info-panel-analysis').append(`
        <div class="navigation-section" style="background-color: #404040; display: flex; justify-content: space-around; padding: 5px; position: absolute; bottom: 90px; left: 75px; right: 75px; border-radius: 5px;">
            <button id="play-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">▶</button>
            <button id="pause-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">❚❚</button>
            <button id="restart-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">↺</button>
            <button id="random-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">﹖</button>
            <button id="orientation-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">◨</button>
        </div>
    `);


    // Navigation
    $('#info-panel-analysis').append(`
        <div class="navigation-section" style="background-color: #b58863; display: flex; justify-content: space-around; padding: 10px; position: absolute; bottom: 20px; left: 20px; right: 20px; border-radius: 5px;">
            <button id="start-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">⇐</button>
            <button id="pre-move-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">←</button>
            <button id="next-move-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">→</button>
            <button id="current-analysis" style="background-color: transparent; color: white; border: none; cursor: pointer; font-size: 1.5em;">⇒</button>
        </div>
    `);

//==============================================================================


//===== CODE PART ==============================================================
//The main() is quite simple: we import our two libraries, initialize our board 
//and menu, and start the mini-games if the buttons are clicked!
//==============================================================================

    $.getScript("/uploads/korentin/chess.js", function() {
        $.getScript ( "/uploads/korentin/chessboard.js", function(){
            
            //===== MENU =======================================================
            
            [board, game] = initBoard('chess-board');

            initMenu(board, game);
            
            //==================================================================
            
            //===== PUZZLE GAME ================================================
            
            $('#puzzle-button').on('click', function() {
                $('#info-panel-puzzle').css('display', 'block');
                startPuzzle(board, game);
            });
            
            //==================================================================
            
            //===== ANALYSIS GAME ==============================================
            
            $('#analysis-button').on('click', function() {
                $('#info-panel-analysis').css('display', 'block');
                document.getElementById("text-bar").textContent = "Analysis game!";
                startAnalysis(board, game);
            });
            
            //==================================================================
        });
    });
    
    //==========================================================================
});

//==============================================================================