Lichess - Detailed Moves

Show brillant, excellent, great and book moves on lichess.org as chess.com does, an updated version of Thomas Sihapanya's version.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name            Lichess - Detailed Moves
// @license         GPL-3.0-only 
// @namespace       https://github.com/sealldeveloper/lichess-better-moves
// @contributionURL https://github.com/sealldeveloper/lichess-better-moves
// @version         0.5
// @description     Show brillant, excellent, great and book moves on lichess.org as chess.com does, an updated version of Thomas Sihapanya's version.
// @author          Seall.DEV & Thomas Sihapnya
// @require         https://greasyfork.org/scripts/47911-font-awesome-all-js/code/Font-awesome%20AllJs.js?version=275337
// @include         /^https\:\/\/lichess\.org\/[a-zA-Z0-9]{8,}/
// @grant           GM.xmlHttpRequest
// @grant           unsafeWindow
// @inject-into     content
// ==/UserScript==
// ==OpenUserJS==
// @author          sealldeveloper
// ==/OpenUserJS==

(function() {
    'use strict';
    const GOOD_MOVE_THRESOLD = 0.6;
    const EXCELLENT_MOVE_THRESOLD = 1;
    const BRILLANT_MOVE_THRESOLD = 2;
    const CHECKMATE_IN_X_MOVES_VALUE = 100;

    let goodMoves = {
        white : {
            'book' : 0,
            'good' : 0,
            'excellent': 0,
            'brillant': 0,
        },
        black : {
            'book' : 0,
            'good' : 0,
            'excellent': 0,
            'brillant': 0,
        }
    }


    /**
     * Wait for the page to load all its elements before running the script
     */
    window.addEventListener('load', function() {

        /**
         * Create click event since .click on elements doesn't work quite well in Chrome
         */
        let clickEvent = document.createEvent('MouseEvents');
        clickEvent.initMouseEvent('mousedown', true, true);

        /**
         * Moves explorer must be opened to detect book moves. If not, user is prompted to run an analysis and reload the page.
         */
        const formAnalysis = document.getElementsByClassName('future-game-analysis')[0];

        if (typeof(formAnalysis) !== 'undefined') {
            alert('Lichess Detailed Moves: Please run an analysis and reload the page when finished. You will then be prompted to accept cross-origin resource : you can safely allow it (it will fetch data for an opening API) !');
        }

        /**
         * Check if users is in analysis page and analysis has been run
         */
        if (document.getElementsByClassName('analyse__tools').length > 0
            && document.getElementsByClassName('future-game-analysis').length === 0
            && document.getElementsByClassName('computer-analysis active').length > 0) {

            const ecoCodesApiUrl = 'https://github.com/sealldeveloper/lichess-detailed-moves/raw/main/data/eco.json';

            /**
             * Loads ECO codes API
             * https://github.com/sealldeveloper/lichess-detailed-moves/raw/main/data/eco.json
             */
            function loadEcoCodesApi() {
                GM.xmlHttpRequest({
                    method: "GET",
                    url:ecoCodesApiUrl,
                    onload: function(response) {
                        lichessGoodMoves(JSON.parse(response.responseText));
                    },
                    onerror: function(err) {
                        alert('Lichess Detailed Moves: The script cannot be launched (maybe you have forbid the access to a cross-origin resource ?) - Refresh the page if you want to start again.');
                    }
                });
            }

            function loadMoves(ecoCodes) {
                let domMoves = document.getElementsByTagName('move');
                let moves = [];
                let previousEval = {
                    value: '+0.0',
                    symbol: '+',
                    absVal: '0.0'
                };

                Object.values(domMoves).forEach(domMove => {
                    if (!domMove.classList.contains('empty')) {
                        if ("undefined" !== typeof(domMove.childNodes)) {

                            domMove.childNodes.forEach(node => {

                                if ('SAN' === node.tagName) {
                                    /**
                                     * Handle opening
                                     */
                                    moves.push(node.innerHTML);

                                    let currentColor = checkColor(moves.length-1);
                                    let currentPgn = createPgnMoves(moves);
                                    let foundOpening = ecoCodes.find(eco => eco.moves.toLowerCase().trim() == currentPgn.toLowerCase().trim());

                                    if (typeof(foundOpening) !== 'undefined') {
                                        handleOpeningMove(node, foundOpening, currentColor);
                                    }

                                    /**
                                     * Handle evaluation
                                     */
                                 		if (!node.innerHTML.endsWith('#')){
                                      let currentEval = {
                                          textValue: domMove.getElementsByTagName('eval')[0].innerHTML,
                                          symbol: domMove.getElementsByTagName('eval')[0].innerHTML.charAt(0)
                                      }

                                      if (currentEval.symbol == '#') {
                                          currentEval.value = currentColor == 'white' ? - CHECKMATE_IN_X_MOVES_VALUE : CHECKMATE_IN_X_MOVES_VALUE;
                                      }
                                      else {
                                          currentEval = {
                                              textValue: currentEval.textValue,
                                              symbol: currentEval.symbol,
                                              value: (currentEval.symbol == '+') ? parseFloat(currentEval.textValue.substring(1)) : 0 - parseFloat(currentEval.textValue.substring(1))
                                          }
                                      }

                                      let delta = currentEval.value - previousEval.value;

                                      let moveText = node.innerHTML;

                                      if ("white" === currentColor) {
                                          if (delta >= GOOD_MOVE_THRESOLD && delta < EXCELLENT_MOVE_THRESOLD) {
                                              node.innerHTML = '<span style="color: #b2f196;">'+
                                                                      moveText+'!?'+
                                                              '</span>';

                                              goodMoves.white.good++;
                                          }
                                          if (delta >= EXCELLENT_MOVE_THRESOLD && delta < BRILLANT_MOVE_THRESOLD) {
                                              node.innerHTML = '<span style="color: #96bc4b;">'+
                                                                      moveText+'!'+
                                                              '</span>';

                                              goodMoves.white.excellent++;
                                          }
                                          if (delta >= BRILLANT_MOVE_THRESOLD) {
                                              node.innerHTML = '<span style="color: #1baca6;">'+
                                                                      moveText+'!!'+
                                                              '</span>';

                                              goodMoves.white.brillant++;
                                          }
                                      }

                                      if ("black" === currentColor) {
                                          if (delta <= -GOOD_MOVE_THRESOLD && delta > -EXCELLENT_MOVE_THRESOLD) {
                                              node.innerHTML = '<span style="color: #b2f196;">'+
                                                                      moveText+'!?'+
                                                              '</span>';

                                              goodMoves.black.good++;
                                          }
                                          if (delta <= -EXCELLENT_MOVE_THRESOLD && delta > -BRILLANT_MOVE_THRESOLD) {
                                              node.innerHTML = '<span style="color: #96bc4b;">'+
                                                                      moveText+'!'+
                                                              '</span>';

                                              goodMoves.black.excellent++;
                                          }
                                          if (delta <= -BRILLANT_MOVE_THRESOLD) {
                                              node.innerHTML = '<span style="color: #1baca6;">'+
                                                                      moveText+'!!'+
                                                              '</span>';

                                              goodMoves.black.brillant++;
                                          }
                                      }

                                      previousEval = currentEval;
                                    }

                                }
                            })
                        }
                    }
                });

                return moves;
            }

            function handleOpeningMove(node, opening, currentColor) {
                const moveText = node.innerHTML;

                node.innerHTML = '<span style="color: #a88865;">'+
                                        moveText+
                                        ' <i class="fas fa-book" style="font-size: 0.7em"></i>'+
                                '</span>'+
                                '<pre style="font-size:0.7em; width:0">'+opening.name+'</pre>';

                node.parentElement.title = opening.name;

                goodMoves[currentColor].book++;
            }

            function checkColor(index) {
                if (0 === index%2) {
                    return "white";
                }
                if (0 !== index%2) {
                    return "black";
                }
            }

            function createPgnMoves(moves) {

                let pgn = '';

                moves.forEach((move, index) => {
                    if ("white" === checkColor(index)) {
                        pgn += (index/2+1) + '. ' + move;
                    }
                    if ("black" === checkColor(index)) {
                        pgn += ' ' + move + ' ';
                    }
                });

                return pgn;
            }

            function showDataInTable() {

                const whiteTable = document.getElementsByClassName('advice-summary__side')[0]
                const blackTable = document.getElementsByClassName('advice-summary__side')[1]
              	function dataPoint(colour,symbol,data,text,table,coloured) {
                    var first = true
                    table.childNodes.forEach(node => {
                        if (node.innerHTML.includes('inaccuracies') && first === true) {
                            const before=node
                            const div = document.createElement('div');
                            if (data !== 0){
                                div.style='color: '+coloured;
                            }
                            div.classList.add('symbol');
                            div.classList.add('advice-summary__error');
                            div.setAttribute('data-color', colour);
                            div.setAttribute('data-symbol', symbol);
                            const strong = document.createElement('strong')
                            strong.innerHTML = data;
                            div.append(strong)
                            div.innerHTML = div.innerHTML+text;
                            table.insertBefore(div,before);
                            first = false
                        }
                    })
                }
                // White
              	dataPoint('white','!!',goodMoves.white.brillant,' brilliancies',whiteTable,'#1baca6')   
              	dataPoint('white','!',goodMoves.white.excellent,' excellencies',whiteTable,'#96bc4b')
              	dataPoint('white','!?',goodMoves.white.good,' greats',whiteTable,'#b2f196')
              	dataPoint('white','Book',goodMoves.white.book,' book',whiteTable,'#a88865')
			
                // Black
                dataPoint('black','!!',goodMoves.black.brillant,' brilliancies',blackTable,'#1baca6')
              	dataPoint('black','!',goodMoves.black.excellent,' excellencies',blackTable,'#96bc4b')
              	dataPoint('black','!?',goodMoves.black.good,' greats',blackTable,'#b2f196')
              	dataPoint('black','Book',goodMoves.black.book,' book',blackTable,'#a88865')
              	
                
            }

            function lichessGoodMoves(ecoCodes) {

                console.log('Lichess Detailed Moves: Successfully started!');

                loadMoves(ecoCodes);
                showDataInTable();
            }

            // Start the app !
            loadEcoCodesApi();

        } // check if users is in analysis page
    }, false); // addEventListener('load', callback)
})(); // Immediately-Invoked Function Expression (function() {})())