Mortal 显示恶手率

Mortal牌谱解析增强脚本

当前为 2023-09-19 提交的版本,查看 最新版本

// ==UserScript==
// @name         Mortal 显示恶手率
// @namespace    https://viayoo.com/
// @version      2.0
// @description  Mortal牌谱解析增强脚本
// @author       mcube-12139
// @author       modify by Miku39
// @run-at       document-idle
// @include      /^https?:\/\/mjai.ekyu.moe\/report\/[A-Za-z0-9-_]+.html$/
// @include      /^https?:\/\/mjai.ekyu.moe\/?([A-Za-z0-9-_]+.html)?$/
// @grant        none
// ==/UserScript==

{
    if(window.location.pathname.startsWith("/report/")) {

        document.getElementsByClassName("collapse")[1].toggleAttribute("open", true); //打开 元数据 选项卡

        //保留n位小数
        function roundFun(value, n) {
            return Math.round(value*Math.pow(10,n))/Math.pow(10,n);
        }

        const badMoveUpperLimit = 5;
        const badMoveUpperLimitCustom = 10;

        let badChooseNum = 0;
        let badChooseNumCustom = 0;

        const lang = document.documentElement.lang;
        const i18nText = {};
        if (lang == "zh-CN") {
            i18nText.badMove = "恶手";
            i18nText.badMoveRatio = "恶手率";
        } else {
            i18nText.badMove = "Bad move";
            i18nText.badMoveRatio = "bad moves/total";
        }
        i18nText.badMoveUp = " (严重鸡打 权重<=5%)";
        i18nText.badMoveDown = " (普通错误 权重5~10%)";
        i18nText.badMoveNull = " ";
        i18nText.badMoveSymbol = "%";
        i18nText.badMoveDiffer = "差值: ";
        //
        i18nText.badMoveDiffer1 = "微差(0~5): ";
        i18nText.badMoveDiffer2 = "小幅差距(5~10): ";
        i18nText.badMoveDiffer3 = "低等差距(10~20): ";
        i18nText.badMoveDiffer4 = "中等差距(20~40): ";
        i18nText.badMoveDiffer5 = "高等差距(40~60): ";
        i18nText.badMoveDiffer6 = "大幅度差距(60~80): ";
        i18nText.badMoveDiffer7 = "压倒性差距(80~100): ";
        //
        i18nText.badMoveSum = " (总计)";

        const orderLosses = document.getElementsByClassName("order-loss");
        for (let i = 0, length = orderLosses.length; i != length; ++i) {
            const orderLoss = orderLosses[i];
            const chosenIndex = parseInt(orderLoss.innerText.substring(2));

            const turnInfo = orderLoss.parentElement;
            const summary = turnInfo.parentElement;
            const collapseEntry = summary.parentElement;

            const details = collapseEntry.lastChild;
            const table = details.firstChild;
            const tbody = table.lastChild;

            const chosenTr = tbody.childNodes[chosenIndex - 1];
            const weightTd = chosenTr.lastChild;
            const intSpan = weightTd.firstChild;

            const chosenWeight = parseInt(intSpan.textContent);

            if (chosenWeight <= badMoveUpperLimit) { //严重恶手
                const badChooseNode = document.createElement("span");
                badChooseNode.innerHTML = ` \u00A0\u00A0\u00A0${i18nText.badMove}${i18nText.badMoveUp}`;
                badChooseNode.style.color = "#f00";
                badChooseNode.style.fontWeight = 900;
                badChooseNode.style.fontSize = "20px";
                turnInfo.appendChild(badChooseNode);

                collapseEntry.style.border = "2px solid #f00";

                badChooseNum++;
            }else if (chosenWeight <= badMoveUpperLimitCustom) { //普通恶手
                const badChooseNode = document.createElement("span");
                badChooseNode.innerHTML = ` \u00A0\u00A0\u00A0${i18nText.badMove}${i18nText.badMoveDown}`;
                badChooseNode.style.color = "#6600FF";
                badChooseNode.style.fontWeight = 700;
                badChooseNode.style.fontSize = "20px";
                turnInfo.appendChild(badChooseNode);

                collapseEntry.style.border = "2px solid #6600FF";

                badChooseNumCustom++;
            }
        } //for

        //const metaData = document.body.childNodes[4];
        const metaData = document.getElementsByClassName("collapse")[1];
        const metaDataDl = metaData.lastChild;
        const version = metaDataDl.childNodes[16];

        const sameRatioDd = metaDataDl.childNodes[15];
        const sameRatioText = sameRatioDd.textContent;
        const chooseNumStr = sameRatioText.substring(sameRatioText.indexOf("/") + 1);
        const chooseNum = parseInt(chooseNumStr);

        metaDataDl.childNodes[14].style.color = "#6600FF"; //rating
        metaDataDl.childNodes[15].style.color = "#6600FF";
        metaDataDl.childNodes[16].style.color = "#6600FF"; //AI 一致率
        metaDataDl.childNodes[17].style.color = "#6600FF";

        const badChooseRatioDt = document.createElement("dt");
        badChooseRatioDt.style.color = "#f00";
        badChooseRatioDt.innerHTML = i18nText.badMoveRatio + i18nText.badMoveNull + badMoveUpperLimit + i18nText.badMoveSymbol;
        
        const badChooseRatioDd = document.createElement("dd");
        badChooseRatioDd.style.color = "#f00";
        badChooseRatioDd.innerHTML = `${badChooseNum}/${chooseNum} = ${(100 * badChooseNum / chooseNum).toFixed(3)}%`;
        metaDataDl.insertBefore(badChooseRatioDd, version);
        metaDataDl.insertBefore(badChooseRatioDt, badChooseRatioDd);

        /* 新增 计算总恶手数 */
        badChooseNumCustom += badChooseNum; //计算总恶手数

        const badChooseRatioDt2 = document.createElement("dt");
        badChooseRatioDt2.style.color = "#f00";
        badChooseRatioDt2.innerText = i18nText.badMoveRatio + i18nText.badMoveNull + badMoveUpperLimitCustom + i18nText.badMoveSymbol;
        
        const badChooseRatioDd2 = document.createElement("dd");
        badChooseRatioDd2.style.color = "#f00";
        badChooseRatioDd2.innerHTML = `${badChooseNumCustom}/${chooseNum} = ${(100 * badChooseNumCustom / chooseNum).toFixed(3)}%`;
        
        metaDataDl.insertBefore(badChooseRatioDd2, version);
        metaDataDl.insertBefore(badChooseRatioDt2, badChooseRatioDd2);

        /* 列出选择权重 */
        const entry = document.getElementsByClassName("collapse entry");
        for (let i = 0, length = entry.length; i != length; ++i) {
            const roleEle = entry[i].getElementsByClassName("role");

            let selfPai = roleEle[0].parentElement;
            let mortalPai = roleEle[1].nextElementSibling;

            if(mortalPai.tagName.toLocaleLowerCase() == 'details') {
                mortalPai = roleEle[1].nextSibling;
            }

            if (Object.prototype.toString.call(selfPai.childNodes[selfPai.childNodes.length -2]) == '[object SVGSVGElement]') {
                if(selfPai.childNodes[selfPai.childNodes.length -2].tagName.toLocaleLowerCase() == 'svg') { //如果有多张牌图片,就使用最后一张牌图片
                    selfPai = selfPai.childNodes[selfPai.childNodes.length -2];
                }
            }

            if(mortalPai.nextElementSibling.tagName.toLocaleLowerCase() == 'svg') { //如果有多张牌图片,就使用最后一张牌图片
                mortalPai = mortalPai.nextElementSibling;
            }
            

            const dataEle = entry[i].getElementsByTagName("tbody")[0].childNodes;

            var selfPaiData = 0;
            var mortalPaiData = 0;
            var map = new Map(); //使用map保证重置循坏后的唯一性

            let j = 0, size = dataEle.length;
            while (j != size) {
                let selfPaiStr = null;
                let mortalPaiStr = null;

                let isResetLoop = false; //是否重置循坏

                if(selfPai != null){
                    if(Object.prototype.toString.call(selfPai) == '[object Text]') {
                        selfPaiStr = selfPai.data;
                    }else{
                        let obj = selfPai.getElementsByClassName("face");
                        if(obj[0] != null){
                            selfPaiStr = obj[0].href.baseVal;
                        }else{ //选择跳过的情况
                            selfPaiStr = selfPai.childNodes[selfPai.childNodes.length -1].data;
                        }
                    }
                }
                if(mortalPai != null){
                    if(Object.prototype.toString.call(mortalPai) == '[object Text]') {
                        mortalPaiStr = mortalPai.data;
                    }else{
                        let obj = mortalPai.getElementsByClassName("face");
                        if(obj[0] != null){
                            mortalPaiStr = obj[0].href.baseVal;
                        }else{ //选择跳过的情况
                            mortalPaiStr = mortalPai.childNodes[mortalPai.childNodes.length -1].data;
                        }
                    }
                }

                let data = dataEle[j].childNodes[2].innerHTML.replace(/<.*?>/g, ""); //过滤html标签, 只保留文字内容

                let obj1 = dataEle[j].childNodes[0].getElementsByClassName("face");
                let dataPaiStr;
                if(obj1[obj1.length - 1] != null) { // 如果有多张牌, 则选择最后一张牌作为对比牌 (主要用于吃的情况、碰杠这些牌都是一样的)
                    dataPaiStr = obj1[obj1.length - 1].href.baseVal;
                }else{
                    dataPaiStr = dataEle[j].childNodes[0].innerHTML;

                    if(map.has(j) == false) {
                        map.set(j, true); //保存当前j的值,防止重复开始循坏
                        j = 0; //如果 有 选择跳过的情况, 则重新开始循坏, 以找到正确的数据
                        isResetLoop = true;
                    }
                }

                if(selfPaiStr == dataPaiStr) { //如果目标操作是自己的操作

                    selfPaiData = data;

                    const span = document.createElement("span");
                    span.innerText = ` \u00A0\u00A0\u00A0` + data;

                    entry[i].insertBefore(span, entry[i].childNodes[3].nextSibling);

                    selfPai = null; //置null, 防止继续计算
                }else if(mortalPaiStr == dataPaiStr) { //如果目标操作是Mortal的操作

                    mortalPaiData = data;

                    const span = document.createElement("span");
                    span.innerText = ` \u00A0\u00A0\u00A0` + data;
                    span.style.marginLeft = "5px";

                    if(Object.prototype.toString.call(mortalPai.nextSibling) == '[object Text]') { //如果有多张牌图片,就使用最后一张牌图片后面的文字的位置
                        mortalPai = mortalPai.nextSibling;
                    }

                    entry[i].insertBefore(span, mortalPai.nextSibling);

                    mortalPai = null; //置null, 防止继续计算
                }

                if(selfPaiStr == mortalPaiStr) { //如果自己选择打出的牌与mortal选择打出的牌相同
                    if(map.has(j) == false) {
                        map.set(j, true); //保存当前j的值,防止重复开始循坏
                        j = 0; //如果 有 选择跳过的情况, 则重新开始循坏, 以找到正确的数据
                        isResetLoop = true;
                    }
                }

                if(selfPai == null && mortalPai == null) { //是否处理完毕
                    break; //跳出循坏
                }
                if(isResetLoop == false){
                    ++j;
                }
            }//for

            /* 计算自己的选择与mortal选择的差值 */
            var differData = roundFun(Math.abs(mortalPaiData - selfPaiData), 5); //保留5位小数
            if (differData != 0) { //忽略自己和mortal打出的牌一样的结果
                var turnInfo = roleEle[0].parentElement.parentElement.childNodes[0];
            
                let newNode = document.createElement("span");

                if (differData < 5) { //微差
                    newNode.style.color = "#000"; //黑色
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer1 + differData;
                }else if (differData < 10) { //小幅差距
                    newNode.style.color = "#996633"; //褐色
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer2 + differData;
                }else if (differData < 20) { //低等差距
                    newNode.style.color = "#009966"; //淡绿
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer3 + differData;
                }else if (differData < 40) { //中等差距
                    newNode.style.color = "#3399FF"; //淡蓝
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer4 + differData;
                }else if (differData < 60) { //高等差距
                    newNode.style.color = "#3333CC"; //深蓝
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer5 + differData;
                }else if (differData < 80) { //大幅度差距
                    newNode.style.color = "#CC0099"; //淡红
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer6 + differData;
                }else{ //压倒性差距
                    newNode.style.color = "#f00"; //红色
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + i18nText.badMoveDiffer7 + differData;
                }
                newNode.style.fontWeight = 400;
                turnInfo.appendChild(newNode);
            }

        }//for
    }else {
        document.getElementsByName("show-rating")[0].checked = true; //默认勾选 显示Rating
    }

    
}



QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址