[HWM] Item sets for AP

Finds cheapest item sets with given total AP

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         [HWM] Item sets for AP
// @namespace    https://greasyfork.org/en/users/242258
// @description  Finds cheapest item sets with given total AP
// @version      0.2
// @author       Alex_2oo8
// @match        https://daily.heroeswm.ru/n/dressroom_arts
// ==/UserScript==

/* global $ arts ch parview lvlp lvln getArtCost urlart pars urlimg */

var container = $("#tb-main-c .text");
container.append($("<hr>"));
container.append($("<br>"));

var controlDiv = $("<div class='set-controls'>Очки амуниции:</div>");
var controlAP = $("<input type='number' min='1' value='10' style='width: 3em;'>");
var controlCnt = $("<input type='number' min='1' value='10' style='width: 5em;'>");
var controlSz = $("<input type='number' min='1' max='9' value='9' style='width: 3em;'>");
var controlBtn = $("<button>Найти</button>");
controlBtn.click(function() { doit(controlAP.val(), controlSz.val(), controlCnt.val()); });

controlDiv.append(controlAP);
controlDiv.append(document.createTextNode("Максимальное количество артефактов:"));
controlDiv.append(controlSz);
controlDiv.append(document.createTextNode("Количество наборов:"));
controlDiv.append(controlCnt);
controlDiv.append(controlBtn);

container.append(controlDiv);
container.append($("<br>"));

$('<style type="text/css">.set-controls input, .set-controls button { margin: 0 1em 0 1em; padding: 0.2em 0.5em; } .set-results { width: 100%; text-align: center; } .set-results th { background-color: #C8C8C8; padding: 1em 0.2em; } .set-results td { padding: 0.3em; } .set-results tbody tr:nth-child(odd) { background: #ECF2F6; } .set-results tbody tr:nth-child(even) { background: #F5F3EA; } .set-results a img { margin: 0.2em; }</style>').appendTo('head');

function doit(ap, maxSz, cnt) {
    cnt *= 2; // sets with different rings will be counted twice

    var i, j, k, s;
    const itemCategories = 9, categoryOrder = { 6: [1], 8: [2], 4: [3], 2: [4], 7: [5], 5: [6], 3: [7], 1: [8, 9] };

    var items = new Array(itemCategories + 1);
    for (i = 1; i <= itemCategories; i++) {
        items[i] = [{ id: [], cost: 0, ap: 0 }];
    }

    for (i in arts) {
        if (categoryOrder.hasOwnProperty(arts[i].tp) && ((ch.g == 1 && arts[i].ta == 'g') || (ch.p == 1 && arts[i].ta == 'p') || (ch.o == 1 && arts[i].ta == 'o') || (ch.v == 1 && arts[i].ta == 'v') || (ch.s == 1 && arts[i].ta == 's') || (ch.r == 1 && arts[i].ta == 'r') || (ch.m == 1 && arts[i].ta == 'm')) && ch[arts[i].tp] == 1 && (arts[i].lv >= lvlp && arts[i].lv <= lvln)) {
            var cost = getItemCostPerBattle(i);

            if (cost == 0 || isNaN(cost)) continue;

            for (j in categoryOrder[arts[i].tp]) {
                items[categoryOrder[arts[i].tp][j]].push({ id: [i], cost: cost, ap: arts[i].oa });
            }
        }
    }

    var maxAPinCategory = [];
    for (i = 1; i <= itemCategories; i++) {
        var maxHere = 0;
        for (j in items[i]) {
            maxHere = Math.max(items[i][j].ap, maxHere);
        }

        maxAPinCategory.push(maxHere);
    }

    maxAPinCategory.sort();
    var maxAP = 0;
    for (i = 0; i < maxSz; i++) maxAP += maxAPinCategory[i];

    if (maxAP < ap) {
        alert("Используя только выбранные артефакты невозможно набрать " + ap + " ОА. Максимум: " + maxAP);

        return;
    }

    // dp[item_categories_processed][total_AP][item_count][set_number] = { min_cost, [ item_set ... ] }

    var dp = new Array(itemCategories + 1);
    for (i = 0; i <= itemCategories; i++) {
        dp[i] = new Array(maxAP + 1);
        for (j = 0; j <= maxAP; j++) {
            dp[i][j] = new Array(maxSz + 1);
            for (s = 0; s <= maxSz; s++) {
                dp[i][j][s] = new Array(cnt);
                for (k = 0; k < cnt; k++) {
                    dp[i][j][s][k] = { cost: Infinity, set: [] };
                }
            }
        }
    }

    dp[0][0][0][0].cost = 0;

    for (i = 1; i <= itemCategories; i++) {
        for (j = 0; j <= maxAP; j++) {
            for (s = 0; s <= maxSz; s++) {
                if (dp[i - 1][j][s][0].cost == Infinity) continue;

                for (k in items[i]) {
                    var newAP = j + items[i][k].ap, newSz = s + items[i][k].id.length;

                    if (newAP > maxAP || newSz > maxSz || dp[i - 1][j][s][0].cost + items[i][k].cost >= dp[i][newAP][newSz][cnt - 1].cost) continue;

                    var oldDP = dp[i][newAP][newSz].slice();

                    for (var p = 0, q = 0, r = 0; r < cnt; r++) {
                        var costP = dp[i - 1][j][s][p].cost + items[i][k].cost;
                        if (costP < oldDP[q].cost) {
                            dp[i][newAP][newSz][r] = { cost: costP, set: dp[i - 1][j][s][p].set.concat(items[i][k].id) };
                            p++;
                        }
                        else {
                            dp[i][newAP][newSz][r] = oldDP[q];
                            q++;
                        }
                    }
                }
            }
        }
    }

    var sets = [];
    for (j = ap; j <= maxAP; j++) {
        for (s = 1; s <= maxSz; s++) {
            for (k = 0; k < cnt; k++) {
                sets.push(dp[itemCategories][j][s][k]);
            }
        }
    }

    sets.sort(function(a, b) {
        if (a.cost == b.cost) return 0;
        return a.cost > b.cost ? 1 : -1;
    });

    cnt /= 2; // Restore the actual value

    clearResults();

    for (k = 0; k < cnt; k++) {
        if (sets[k].cost == Infinity) break;
        if (k > 0 && sameSets(sets[k].set, sets[k - 1].set)) {
            cnt++;
            continue;
        }

        showSet(sets[k].set);
    }
}

function sameSets(one, two) {
    if (one.length != two.length) return false;

    one = one.slice().sort();
    two = two.slice().sort();
    for (var i = 0; i < one.length; i++) {
        if (one[i] != two[i]) return false;
    }

    return true;
}

var resultTable = null;

function clearResults() {
    if (resultTable == null) {
        container.append($("<hr>"));
        container.append($("<br>"));

        var table = $("<table class='set-results'><thead><tr><th>#</th><th>Артефакты</th><th>ОА</th><th>Стоимость боя</th><th>Параметры</th></tr></thead></table>");
        resultTable = $("<tbody>");
        table.append(resultTable);
        container.append(table);
    }

    resultTable.empty();
}

function showSet(set) {
    var tr = $("<tr><td>" + (resultTable.children().length + 1) + "</td></tr>");
    var setTd = $("<td>");
    var ap = 0, cost = 0, par;
    var params = { };
    for (var i in set) {
        var id = set[i];

        ap += arts[id].oa;
        cost += getItemCostPerBattle(id);

        for (par in arts[id].p) {
            if (params.hasOwnProperty(par) == false) {
                params[par] = 0;
            }

            params[par] += arts[id].p[par];
        }

        var a = $("<a href='http://www.heroeswm.ru/art_info.php?id=" + (arts[id].tn ? arts[id].tn : id) + "' target='_blank'><img src='" + urlart + ((arts[id].ts && arts[id].ts != "") ? arts[id].ts + "/" : "") + arts[id].tf + (arts[id].png != 1 ? "_s.jpg" : ".png") + "' title='" + arts[id].tt + "'></a>");
        setTd.append(a);
    }

    var paramTd = $("<td>");
    for (par in pars) {
        if (params.hasOwnProperty(par) == false) continue;
        paramTd.append($("<img src='" + urlimg + pars[par].img + "' align='absmiddle' style='margin-left: 0.3em; width: 24px; height: 24px;' title='" + pars[par].c + "'>"));
        paramTd.append(document.createTextNode(params[par] + (pars[par].hasOwnProperty("t") ? (pars[par].t) : "")));
    }

    tr.append(setTd);
    tr.append($("<td>" + ap + "</td>"));
    tr.append($("<td><img align='absmiddle' width='24' height='24' src='https://dcdn3.heroeswm.ru/i/gold.gif' border='0' title='Золото' alt=''>" + Math.round(cost * 100) / 100 + "</td>"));
    tr.append(paramTd);

    resultTable.append(tr);
}

function getItemCostPerBattle(i) {
    var cost;
    if (arts[i].hasOwnProperty("cp")) {
        cost = arts[i].cp;
    }
    else {
        cost = getArtCost(i);
    }

    cost /= arts[i].st;

    return cost;
}