BvS Crank Helper

Displays average success chances for selected Crank level.

// ==UserScript==
// @name           BvS Crank Helper
// @include        http*://*animecubed.com/billy/bvs/pages/main.html
// @include        http*://*animecubed.com/billy/bvs/themesdifficulty.html
// @include        http*://*animecubedgaming.com/billy/bvs/pages/main.html
// @include        http*://*animecubedgaming.com/billy/bvs/themesdifficulty.html
// @namespace      North
// @description    Displays average success chances for selected Crank level.
// @version        1.2
// @history        1.2 New domain - animecubedgaming.com - Channel28
// @history        1.1 Now https compatible (Updated by Channel28)
// @history        1.0 Initial Release
// @grant          none
// ==/UserScript==

function CrankHelper(){
    var name = document.evaluate("//input[@name='player' and @type='hidden']", document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue.value;
    var tbody;

    var addStyle = function(css) {
        var head = document.getElementsByTagName("head")[0];
        if (!head)
            return;
        var style = document.createElement("style");
        style.type = "text/css";
        style.textContent = css;
        head.appendChild(style);
    }

    var binomialCoefficient = function(n, k){
        if (k > n || k < 0)
            return 0;
        k = Math.max(k, n - k);
        var i = 1;
        var j = k + 1;
        var c = 1;
        while (j <= n)
            c *= j++ / i++;
        return c;
    }

    var binomdist = function(k, n, p, cumulative){
        if (cumulative) {
            var sum = 0;
            for (var i = 0; i <= k; i++)
                sum += binomdist(i, n, p, false);
            return sum;
        }
        return binomialCoefficient(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k);
    }

    var successChance = function(lvl, str, rng, suc, dif, sucx){
        var req = sucx - suc;
        if (req <= 0)
            return 1;
        if (dif > rng)
            return 0;
        if (req > lvl)
            return 0;

        var prob = (str + rng - dif + 1) / rng;
        if (prob >= 1)
            return 1;

        return Math.min(0.9999, 1 - binomdist(req - 1, lvl, prob, true));
    }

    var strip = function(s){
        s = s.replace(/^\s+/, "");
        s = s.replace(/\s+$/, "");
        s = s.replace(/\s+/g, " ");
        return s;
    }

    var percent = function(n){
        if (n == 1.0)
            return "100%";
        else if (n == 0.0)
            return "0%";
        var p = Math.round(n * 1000) / 10;
        if (p > 99.9)
            return ">99.9%";
        else if (p < 0.1)
            return "<0.1%";
        return p + "%";
    }

    var mainStats = function(){
        var snap = document.evaluate("//td[contains(text(),'jutsu:')]", document, null, 7, null);
        var stats = [];
        for (var i = 0; i < snap.snapshotLength; i++) {
            var node = snap.snapshotItem(i);
            var stat = node.childNodes[0].textContent;
            stat = stat.slice(0, stat.length - 2);
            var lvl, str, rng, suc;
            node = strip(node.childNodes[1].textContent).match(/(\d+)( \(([+-]\d+)\))?(Strength:([+-]\d+))? Range: 1-(\d+)(Free Successes: (\d+))?/);
            if (node){
                lvl = parseInt(node[1]) || 0;
                lvl += parseInt(node[3]) || 0;
                str = parseInt(node[5]) || 0;
                rng = parseInt(node[6]) || 0;
                suc = parseInt(node[8]) || 0;
            }

            stats[i] = [stat, lvl, str, rng, suc].join(',');
        }

        localStorage.setItem("BvS_CrankHelper_" + name, stats.join(';'));
    }

    var singleDiff = function(type, stats, crank, dif, suc){
        dif += crank;
        suc += crank;
        var stat = stats["Taijutsu"];
        var taic = successChance(stat[0], stat[1], stat[2], stat[3], dif, suc);
        stat = stats["Ninjutsu"];
        var ninc = successChance(stat[0], stat[1], stat[2], stat[3], dif, suc);
        stat = stats["Genjutsu"];
        var genc = successChance(stat[0], stat[1], stat[2], stat[3], dif, suc);

        var tr = '<tr><td colspan="7"/></tr>';
        tr += '<tr><td>' + type + '</td><td colspan="2" class="genc">' + percent(genc) + '</td><td colspan="2" class="ninc">' + percent(ninc) + '</td><td colspan="2" class="taic">' + percent(taic) + '</td></tr>';
        tbody.innerHTML += tr;
    }

    var doubleDiff = function(type, stats, crank, dif1, suc1, dif2, suc2){
        dif1 += crank;
        dif2 += crank;
        suc1 += crank;
        suc2 += crank;
        var stat = stats["Taijutsu"];
        var taic1 = successChance(stat[0], stat[1], stat[2], stat[3], dif1, suc1);
        var taic2 = successChance(stat[0], stat[1], stat[2], stat[3], dif2, suc2);
        stat = stats["Ninjutsu"];
        var ninc1 = successChance(stat[0], stat[1], stat[2], stat[3], dif1, suc1);
        var ninc2 = successChance(stat[0], stat[1], stat[2], stat[3], dif2, suc2);
        stat = stats["Genjutsu"];
        var genc1 = successChance(stat[0], stat[1], stat[2], stat[3], dif1, suc1);
        var genc2 = successChance(stat[0], stat[1], stat[2], stat[3], dif2, suc2);

        var tr = '<tr><td colspan="7"/></tr>';
        tr += '<tr><td rowspan="2">' + type + '</td><td colspan="2" class="genc">' + percent(genc1) + '</td><td colspan="2" class="ninc">' + percent(ninc1) + '</td><td colspan="2" class="taic">' + percent(taic1) + '</td></tr>';
        tr += '<tr><td class="ninc">' + percent(ninc2) + '</td><td class="taic">' + percent(taic2) + '</td><td class="genc">' + percent(genc2) + '</td><td class="taic">' + percent(taic2) + '</td><td class="genc">' + percent(genc2) + '</td><td class="ninc">' + percent(ninc2) + '</td></tr>';
        tbody.innerHTML += tr;
    }
	
    var douSimple = function(type, stats, crank, dif, suc){
        dif += crank;
        suc += crank;
        var stat = stats["Doujutsu"];
        var douc = successChance(stat[0], stat[1], stat[2], stat[3], dif, suc);
        var tr = '<tr><td colspan="7"/></tr>';
        tr +=  '<tr><td>' + type + '</td><td colspan="6" class="douc">' + percent(douc) + '</td></tr>';
        tbody.innerHTML += tr;
    }
	
    var douDouble = function(type, stats, crank, difd, sucd, difg, sucg, difn, sucn, dift, suct){
        difd += crank;
        difg += crank;
        difn += crank;
        dift += crank;
        sucd += crank;
        sucg += crank;
        sucn += crank;
        suct += crank;
        var stat = stats["Doujutsu"];
        var douc = successChance(stat[0], stat[1], stat[2], stat[3], difd, sucd);
        stat = stats["Taijutsu"];
        var taic = successChance(stat[0], stat[1], stat[2], stat[3], dift, suct);
        stat = stats["Ninjutsu"];
        var ninc = successChance(stat[0], stat[1], stat[2], stat[3], difn, sucn);
        stat = stats["Genjutsu"];
        var genc = successChance(stat[0], stat[1], stat[2], stat[3], difg, sucg);
        
        var tr = '<tr><td colspan="7"/></tr>';
        tr +=  '<tr><td rowspan="2">' + type + '</td><td colspan="6" class="douc">' + percent(douc) + '</td></tr>';
        tr += '<tr><td colspan="2" class="genc">' + percent(genc) + '</td><td colspan="2" class="ninc">' + percent(ninc) + '</td><td colspan="2" class="taic">' + percent(taic) + '</td></tr>';
        tbody.innerHTML += tr;
    }

    var douOther = function(type, stats, crank, difd1, sucd1, dif1, suc1, difd2, sucd2, dif2, suc2){
        difd1 += crank;
        difd2 += crank;
        dif1 += crank;
        dif2 += crank;
        sucd1 += crank;
        sucd2 += crank;
        suc1 += crank;
        suc2 += crank;
        var stat = stats["Doujutsu"];
        var douc1 = successChance(stat[0], stat[1], stat[2], stat[3], difd1, sucd1);
        var douc2 = successChance(stat[0], stat[1], stat[2], stat[3], difd2, sucd2);
        stat = stats["Taijutsu"];
        var taic1 = successChance(stat[0], stat[1], stat[2], stat[3], dif1, suc1);
        var taic2 = successChance(stat[0], stat[1], stat[2], stat[3], dif2, suc2);
        stat = stats["Ninjutsu"];
        var ninc = successChance(stat[0], stat[1], stat[2], stat[3], dif2, suc2);
        stat = stats["Genjutsu"];
        var genc = successChance(stat[0], stat[1], stat[2], stat[3], dif1, suc1);

        var tr = '<tr><td colspan="7"/></tr>';
        tr +=  '<tr><td rowspan="5">' + type + '</td><td colspan="6" class="douc">' + percent(douc1) + '</td></tr>';
        tr += '<tr><td colspan="2" class="genc">' + percent(genc) + '</td><td colspan="2" class="ninc"> - </td><td colspan="2" class="taic">' + percent(taic1) + '</td></tr>';
        tr += '<tr><td colspan="6"/></tr>';
        tr +=  '<tr><td colspan="6" class="douc">' + percent(douc2) + '</td></tr>';
        tr += '<tr><td colspan="2" class="genc"> - </td><td colspan="2" class="ninc">' + percent(ninc) + '</td><td colspan="2" class="taic">' + percent(taic2) + '</td></tr>';
        tbody.innerHTML += tr;
    }

    var themesDifficulty = function(){
        var crank = parseInt(document.getElementsByName("cranknumber")[0].value);
        var stats = [];
        var temp = localStorage.getItem("BvS_CrankHelper_" + name).split(';');
        
        for(var i = 0; i<temp.length; i++){
            var temp2 = temp[i].split(',');
            stats[temp2[0]] = [temp2[1] * 1, temp2[2] * 1, temp2[3] * 1, temp2[4] * 1];
        }
        tbody.innerHTML = '<tr><th class="hh">Mission Type</th><th colspan="2" class="genh">Gen</th><th colspan="2" class="ninh">Nin</th><th colspan="2" class="taih">Tai</th></tr>';
        singleDiff("D-Rank", stats, crank, 7, 1);
        singleDiff("C-Rank", stats, crank, 8, 3);
        doubleDiff("B-Rank", stats, crank, 10, 8, 9, 7);
        doubleDiff("A-Rank", stats, crank, 13, 9, 5, 11);
        doubleDiff("AA-Rank", stats, crank, 18, 12, 15, 16);
        doubleDiff("S-Rank", stats, crank, 22, 13, 18, 14);
        doubleDiff("Monochrome", stats, crank, 20, 17, 18, 18);
        singleDiff("R00t AA-Rank", stats, crank, 25, 25);
        douSimple("BurgerNinja", stats, crank, 4, 5);
        douOther("PizzaWitch", stats, crank, 16, 23, 28, 26, 17, 29, 25, 25);
        douDouble("PizzaWitch-High", stats, crank, 25, 27, 30, 40, 40, 35, 30, 40);
        douDouble("Witching Hours", stats, crank, 26, 30, 30, 25, 30, 23, 23, 30);

    }
    return{
        init:function(){
            if(/main\.html/.test(location.pathname)){
                mainStats();
            }
            if(/themesdifficulty\.html/.test(location.pathname)){
                addStyle([
                    ".crankHelperTable { border-collapse: collapse; border: 2px solid black; margin: auto; text-align: center; }",
                    ".crankHelperTable tr th { padding: 0.2em; border: 1px solid #888;}",
                    ".crankHelperTable tr { background-color: #F4F9FE; }",
                    ".crankHelperTable tr td { border: 1px solid #888; padding: 2px; }",
                    ".taic { background-color: #071E4A; color: white;}",
                    ".ninc { background-color: #4A0707; color: white;}",
                    ".genc { background-color: #124A07; color: white;}",
                    ".douc { background-color: #4A4A07; color: white;}",
                    ".hh { background: #D4DEEF url(data:image/gif;base64,R0lGODlhAQAUAMQAAPT3+9Te79rj8eju9vn6/d/m8+Ho9Pf5/Ovv9/H1+uXr9dXf79fh8P3+/vz8/u7y+dzk8v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABABQAAAUQYCQ2DnEAyYMMilFAArMEIQA7) top center repeat-x; }",
                    ".douh { color: white; background: #4A4A07 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAKCAIAAAD6sKMdAAAACXBIWXMAAAsTAAALEwEAmpwYAAAALklEQVR42gXBQQoAIAhFQX3Q/U8ZhvDFTbuiZuy9wd5ON1SB5ETAnM5aRuZFOh+K0BSu6v8ibAAAAABJRU5ErkJggg==) repeat-x left top;}",
                    ".taih { color: white; background: #071E4A url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAKCAIAAAD6sKMdAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMElEQVR42gXBiQoAEBBAQfvKj/tXkXKkHFvEjLHuIWNAbVAL0jPMgKjHaOSuxNn5A21aFILfIZVwAAAAAElFTkSuQmCC) repeat-x left top;}",
                    ".ninh { color: white; background: #4A0707 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAKCAIAAAD6sKMdAAAACXBIWXMAAAsTAAALEwEAmpwYAAAALUlEQVR42gXBQQoAIAhFQf8D73/KKAKjTTslZ+y78yQucICQmMCQWGbsKiKzAdgHDWs7Xvp7AAAAAElFTkSuQmCC) repeat-x left top;}",
                    ".genh { color: white; background: #124A07 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAKCAIAAAD6sKMdAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMUlEQVR42gXBQQ4AEAwAwXYT7/H/BwmREBcH0qoZyZGIrcSCN+ENxSt4UawJtztn2Ad2ABR2S9mVngAAAABJRU5ErkJggg==) repeat-x left top;}"
                    ].join("\n"));

                var snap = document.evaluate("//table[@cellpadding = 4]/tbody", document, null, 7, null);
                var successTable = document.createElement("table");
                tbody = document.createElement("tbody");
                successTable.appendChild(tbody);
                snap.snapshotItem(0).appendChild(document.createElement("tr").appendChild(document.createElement("td").appendChild(successTable)));
                successTable.className="crankHelperTable";
                
                themesDifficulty();
                var combobox = document.getElementsByName("cranknumber")[0];
                if(combobox.wrappedJSObject)
                    combobox = combobox.wrappedJSObject;
                combobox.onchange = themesDifficulty;
            }
        }
    }
}

CrankHelper().init();