From 6ba2383e2f57ca8ab201ae1a933613b0bd88ab3a Mon Sep 17 00:00:00 2001 From: hppeng Date: Sat, 16 Jul 2022 22:47:19 -0700 Subject: [PATCH 01/14] Add debugging printouts to skillpoint computation and overall compute --- js/builder.js | 5 +++++ js/builder_graph.js | 1 + js/computation_graph.js | 18 +++++++++++------- js/load.js | 1 - js/load_ing.js | 1 - js/skillpoints.js | 3 +++ 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/js/builder.js b/js/builder.js index 215ef22..e45c696 100644 --- a/js/builder.js +++ b/js/builder.js @@ -390,7 +390,12 @@ window.onerror = function(message, source, lineno, colno, error) { }; (async function() { + const start = Date.now(); let load_promises = [ load_init(), load_ing_init(), load_tome_init() ]; await Promise.all(load_promises); + const codestart = Date.now(); init(); + const end = Date.now(); + console.log(`builder calculation took ${(end-codestart)/ 1000} seconds.`); + console.log(`builder total took ${(end-start)/ 1000} seconds.`); })(); diff --git a/js/builder_graph.js b/js/builder_graph.js index f836986..dc52c1a 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1143,5 +1143,6 @@ function builder_graph_init() { // this will propagate the update to the `stat_agg_node`, and then to damage calc console.log("Set up graph"); + let INPUT_UPDATE = true; } diff --git a/js/computation_graph.js b/js/computation_graph.js index 1f13851..d32554d 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -174,20 +174,24 @@ class ValueCheckComputeNode extends ComputeNode { } +let INPUT_UPDATE = false; + /** * Schedule a ComputeNode to be updated. * * @param node : ComputeNode to schedule an update for. */ function calcSchedule(node, timeout) { - if (node.update_task !== null) { - clearTimeout(node.update_task); + if (INPUT_UPDATE) { + if (node.update_task !== null) { + clearTimeout(node.update_task); + } + node.mark_dirty(); + node.update_task = setTimeout(function() { + node.update(); + node.update_task = null; + }, timeout); } - node.mark_dirty(); - node.update_task = setTimeout(function() { - node.update(); - node.update_task = null; - }, timeout); } class PrintNode extends ComputeNode { diff --git a/js/load.js b/js/load.js index c425c97..afdc85c 100644 --- a/js/load.js +++ b/js/load.js @@ -254,5 +254,4 @@ function init_maps() { redirectMap.set(item.id, item.remapID); } } - console.log(itemMap); } diff --git a/js/load_ing.js b/js/load_ing.js index ffc4c05..e5d37f4 100644 --- a/js/load_ing.js +++ b/js/load_ing.js @@ -232,5 +232,4 @@ function init_ing_maps() { recipeList.push(recipe["name"]); recipeIDMap.set(recipe["id"],recipe["name"]); } - console.log(ingMap); } diff --git a/js/skillpoints.js b/js/skillpoints.js index 5748a00..4c15a1d 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -1,4 +1,5 @@ function calculate_skillpoints(equipment, weapon) { + const start = Date.now(); // Calculate equipment equipping order and required skillpoints. // Return value: [equip_order, best_skillpoints, final_skillpoints, best_total]; let fixed = []; @@ -205,5 +206,7 @@ function calculate_skillpoints(equipment, weapon) { // best_skillpoints: manually assigned (before any gear) // final_skillpoints: final totals (5 individ) // best_total: total skillpoints assigned (number) + const end = Date.now(); + console.log(`skillpoint calculation took ${(end-start)/ 1000} seconds.`); return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts]; } From 3294494c539154e5f87ecf1417a9962b52d9e428 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sat, 16 Jul 2022 22:48:28 -0700 Subject: [PATCH 02/14] Display sp compute times in html --- js/skillpoints.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index 4c15a1d..c304438 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -207,6 +207,8 @@ function calculate_skillpoints(equipment, weapon) { // final_skillpoints: final totals (5 individ) // best_total: total skillpoints assigned (number) const end = Date.now(); - console.log(`skillpoint calculation took ${(end-start)/ 1000} seconds.`); + const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`; + console.log(output_msg); + document.getElementById('stack-box').textContent = output_msg; return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts]; } From efad0b5fdd904f250aeb210a2af71d09714452cb Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 03:38:12 -0500 Subject: [PATCH 03/14] Use direct property access to speed everything up --- js/builder.js | 7 +++++-- js/builder_graph.js | 2 +- js/skillpoints.js | 28 ++++++++++++++++------------ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/js/builder.js b/js/builder.js index e45c696..b10b071 100644 --- a/js/builder.js +++ b/js/builder.js @@ -396,6 +396,9 @@ window.onerror = function(message, source, lineno, colno, error) { const codestart = Date.now(); init(); const end = Date.now(); - console.log(`builder calculation took ${(end-codestart)/ 1000} seconds.`); - console.log(`builder total took ${(end-start)/ 1000} seconds.`); + const calc_str = `builder calculation took ${(end-codestart)/ 1000} seconds.`; + const total_str = `builder total took ${(end-start)/ 1000} seconds.`; + console.log(calc_str); + console.log(total_str); + document.getElementById('stack-box').textContent += calc_str + total_str; })(); diff --git a/js/builder_graph.js b/js/builder_graph.js index dc52c1a..c3ff320 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1143,6 +1143,6 @@ function builder_graph_init() { // this will propagate the update to the `stat_agg_node`, and then to damage calc console.log("Set up graph"); - let INPUT_UPDATE = true; + INPUT_UPDATE = true; } diff --git a/js/skillpoints.js b/js/skillpoints.js index c304438..1001de8 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -6,16 +6,22 @@ function calculate_skillpoints(equipment, weapon) { let consider = []; let noboost = []; let crafted = []; + weapon.skillpoints = weapon.get('skillpoints'); + weapon.reqs = weapon.get('reqs'); + weapon.set = weapon.get('set'); for (const item of equipment) { + item.skillpoints = item.get('skillpoints'); + item.reqs = item.get('reqs'); + item.set = item.get('set'); if (item.get("crafted")) { crafted.push(item); } - else if (item.get("reqs").every(x => x === 0) && item.get("skillpoints").every(x => x >= 0)) { + else if (item.get("reqs").every(x => x === 0) && item.skillpoints.every(x => x >= 0)) { // All reqless item without -skillpoints. fixed.push(item); } // TODO hack: We will treat ALL set items as unsafe :( - else if (item.get("skillpoints").every(x => x === 0) && item.get("set") === null) { + else if (item.skillpoints.every(x => x === 0) && item.set === null) { noboost.push(item); } else { @@ -24,10 +30,10 @@ function calculate_skillpoints(equipment, weapon) { } function apply_skillpoints(skillpoints, item, activeSetCounts) { for (let i = 0; i < 5; i++) { - skillpoints[i] += item.get("skillpoints")[i]; + skillpoints[i] += item.skillpoints[i]; } - const setName = item.get("set"); + const setName = item.set; if (setName) { // undefined/null means no set. let setCount = activeSetCounts.get(setName); let old_bonus = {}; @@ -52,17 +58,17 @@ function calculate_skillpoints(equipment, weapon) { let applied = [0, 0, 0, 0, 0]; let total = 0; for (let i = 0; i < 5; i++) { - if (item.get("skillpoints")[i] < 0 && skillpoint_min[i]) { - const unadjusted = skillpoints[i] + item.get("skillpoints")[i]; + if (item.skillpoints[i] < 0 && skillpoint_min[i]) { + const unadjusted = skillpoints[i] + item.skillpoints[i]; const delta = skillpoint_min[i] - unadjusted; if (delta > 0) { applied[i] += delta; total += delta; } } - if (item.get("reqs")[i] == 0) continue; - skillpoint_min[i] = Math.max(skillpoint_min[i], item.get("reqs")[i] + item.get("skillpoints")[i]); - const req = item.get("reqs")[i]; + if (item.reqs[i] == 0) continue; + skillpoint_min[i] = Math.max(skillpoint_min[i], item.reqs[i] + item.skillpoints[i]); + const req = item.reqs[i]; const cur = skillpoints[i]; if (req > cur) { const diff = req - cur; @@ -71,7 +77,7 @@ function calculate_skillpoints(equipment, weapon) { } } - const setName = item.get("set"); + const setName = item.set; if (setName) { // undefined/null means no set. const setCount = activeSetCounts.get(setName); if (setCount) { @@ -113,9 +119,7 @@ function calculate_skillpoints(equipment, weapon) { // Try every combination and pick the best one. for (let permutation of perm(consider)) { let activeSetCounts = new Map(static_activeSetCounts); - let has_skillpoint = allFalse.slice(); - permutation = permutation.concat(noboost); let skillpoints_applied = [0, 0, 0, 0, 0]; From 6c7c274f8196bae9933dbfda1853b8ff1e2756ce Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 12:40:37 -0700 Subject: [PATCH 04/14] itemLists is now an object also some test code for scc --- js/builder.js | 4 ++-- js/load.js | 14 +++++++------- js/skillpoints.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/js/builder.js b/js/builder.js index b10b071..24b82cf 100644 --- a/js/builder.js +++ b/js/builder.js @@ -164,7 +164,7 @@ function init_autocomplete() { let item_arr = []; if (eq == 'weapon') { for (const weaponType of weapon_keys) { - for (const weapon of itemLists.get(weaponType)) { + for (const weapon of itemLists[weaponType]) { let item_obj = itemMap.get(weapon); if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { continue; @@ -176,7 +176,7 @@ function init_autocomplete() { } } } else { - for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) { + for (const item of itemLists[eq.replace(/[0-9]/g, '')]) { let item_obj = itemMap.get(item); if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { continue; diff --git a/js/load.js b/js/load.js index afdc85c..eceed6d 100644 --- a/js/load.js +++ b/js/load.js @@ -10,7 +10,12 @@ let sets = new Map(); let itemMap; let idMap; let redirectMap; -let itemLists = new Map(); +let itemLists = {}; +// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. +for (const it of itemTypes) { + itemLists[it] = []; +} + /* * Load item set from local DB. Calls init() on success. */ @@ -189,11 +194,6 @@ async function load_init() { }); } -// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. -for (const it of itemTypes) { - itemLists.set(it, []); -} - let none_items = [ ["armor", "helmet", "No Helmet"], ["armor", "chestplate", "No Chestplate"], @@ -241,7 +241,7 @@ function init_maps() { //console.log(items); for (const item of items) { if (item.remapID === undefined) { - itemLists.get(item.type).push(item.displayName); + itemLists[item.type].push(item.displayName); itemMap.set(item.displayName, item); if (none_items.includes(item)) { idMap.set(item.id, ""); diff --git a/js/skillpoints.js b/js/skillpoints.js index 1001de8..86b130c 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -117,6 +117,7 @@ function calculate_skillpoints(equipment, weapon) { let allFalse = [0, 0, 0, 0, 0]; if (consider.length > 0 || noboost.length > 0 || crafted.length > 0) { // Try every combination and pick the best one. + construct_scc_graph(consider); for (let permutation of perm(consider)) { let activeSetCounts = new Map(static_activeSetCounts); let has_skillpoint = allFalse.slice(); @@ -216,3 +217,45 @@ function calculate_skillpoints(equipment, weapon) { document.getElementById('stack-box').textContent = output_msg; return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts]; } + +function construct_scc_graph(items_to_consider) { + let nodes = []; + for (const item of items_to_consider) { + nodes.push({item: item, children: [], parents: [], visited: false}); + } + let root_node = { + children: nodes, + parents: [], + visited: false + }; + // Dependency graph construction. + for (const node_a of nodes) { + const {item: a, children: a_children} = node_a; + for (const node_b of nodes) { + const {item: b, parents: b_parents} = node_b; + for (let i = 0; i < 5; ++i) { + if (b.reqs[i] < a.reqs[i] && b.skillpoints[i]) { + a_children.push(node_b); + b_parents.push(node_a); + break; + } + } + } + } + const res = [] + /* + * SCC graph construction. + * https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm + */ + function visit(u, res) { + if (u.visited) { return; } + u.visited = true; + for (const child of u.children) { + if (!child.visited) { visit(child, res); } + } + res.push(u); + } + visit(root_node, res); + res.reverse(); + console.log(res); +} From da6a422d79356ffbdf31bdd97bc4740f5e5e9b1b Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 15:10:50 -0500 Subject: [PATCH 05/14] Change ingred and item `Map`s to plain object access --- js/build_encode_decode.js | 2 +- js/builder.js | 6 +-- js/builder_graph.js | 2 +- js/craft.js | 2 +- js/crafter.js | 13 +------ js/customizer.js | 4 +- js/display.js | 6 +-- js/item.js | 3 +- js/load.js | 33 ++++++----------- js/load_ing.js | 78 +++++++++++++++++++-------------------- js/skillpoints.js | 2 +- 11 files changed, 65 insertions(+), 86 deletions(-) diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index a5f88d0..b4619f5 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -1,7 +1,7 @@ let player_build; let build_powders; -function getItemNameFromID(id) { return idMap.get(id); } +function getItemNameFromID(id) { return idMap[id]; } function getTomeNameFromID(id) { return tomeIDMap.get(id); } function parsePowdering(powder_info) { diff --git a/js/builder.js b/js/builder.js index 24b82cf..d5c7b32 100644 --- a/js/builder.js +++ b/js/builder.js @@ -165,7 +165,7 @@ function init_autocomplete() { if (eq == 'weapon') { for (const weaponType of weapon_keys) { for (const weapon of itemLists[weaponType]) { - let item_obj = itemMap.get(weapon); + let item_obj = itemMap[weapon]; if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { continue; } @@ -177,7 +177,7 @@ function init_autocomplete() { } } else { for (const item of itemLists[eq.replace(/[0-9]/g, '')]) { - let item_obj = itemMap.get(item); + let item_obj = itemMap[item]; if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { continue; } @@ -220,7 +220,7 @@ function init_autocomplete() { class: "scaled-font search-item", selected: "dark-5", element: (item, data) => { - item.classList.add(itemMap.get(data.value).tier); + item.classList.add(itemMap[data.value].tier); }, }, events: { diff --git a/js/builder_graph.js b/js/builder_graph.js index c3ff320..db7c231 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -154,7 +154,7 @@ class ItemInputNode extends InputNode { let item; if (item_text.slice(0, 3) == "CI-") { item = getCustomFromHash(item_text); } else if (item_text.slice(0, 3) == "CR-") { item = getCraftFromHash(item_text); } - else if (itemMap.has(item_text)) { item = new Item(itemMap.get(item_text)); } + else if (itemMap[item_text]) { item = new Item(itemMap[item_text]); } else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); } if (item) { diff --git a/js/craft.js b/js/craft.js index db30aef..59c1446 100644 --- a/js/craft.js +++ b/js/craft.js @@ -34,7 +34,7 @@ function getCraftFromHash(hash) { if (version === "1") { let ingreds = []; for (let i = 0; i < 6; i ++ ) { - ingreds.push( expandIngredient(ingMap.get(ingIDMap.get(Base64.toInt(name.substring(2*i,2*i+2))))) ); + ingreds.push( expandIngredient(ingMap[ingIDMap[Base64.toInt(name.substring(2*i,2*i+2))]]) ); } let recipe = expandRecipe(recipeMap.get(recipeIDMap.get(Base64.toInt(name.substring(12,14))))); diff --git a/js/crafter.js b/js/crafter.js index 865b740..f4f461b 100644 --- a/js/crafter.js +++ b/js/crafter.js @@ -32,15 +32,6 @@ let player_craft; function init_crafter() { //no ing - - console.log("all ingredients"); - console.log(ingMap); - console.log("all recipes"); - console.log(recipeMap); - /*console.log(ingList); - console.log(recipeList); - console.log(ingIDMap); - console.log(recipeIDMap);*/ try { document.getElementById("recipe-choice").addEventListener("change", (event) => { updateMaterials(); @@ -149,7 +140,7 @@ function calculateCraft() { for (i = 1; i < 7; i++) { console.log("ing-choice-"+i); // console.log(getValue("ing-choice-"+i)); - getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i)))); + getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap["No Ingredient"])) : ingreds.push(expandIngredient(ingMap[getValue("ing-choice-" + i)])); } let atkSpd = "NORMAL"; //default attack speed will be normal. for (const b of ["slow-atk-button", "normal-atk-button", "fast-atk-button"]) { @@ -214,7 +205,7 @@ function decodeCraft(ing_url_tag) { if (version === "1") { ingreds = []; for (let i = 0; i < 6; i ++ ) { - setValue("ing-choice-"+(i+1), ingIDMap.get(Base64.toInt(tag.substring(2*i,2*i+2)))); + setValue("ing-choice-"+(i+1), ingIDMap[Base64.toInt(tag.substring(2*i,2*i+2))]); //console.log(Base64.toInt(tag.substring(2*i,2*i+2))); } recipe = recipeIDMap.get(Base64.toInt(tag.substring(12,14))); diff --git a/js/customizer.js b/js/customizer.js index 059a849..a916bca 100644 --- a/js/customizer.js +++ b/js/customizer.js @@ -331,7 +331,7 @@ function populateFields() { class_list.appendChild(el); } let item_list = document.getElementById("base-list"); - for (const name of itemMap.keys()) { + for (const name of Object.keys(itemMap)) { let el = document.createElement("option"); el.value = name; item_list.appendChild(el); @@ -368,7 +368,7 @@ function useBaseItem(elem) { let baseItem; //Check items db. - for (const [name,itemObj] of itemMap) { + for (const [name,itemObj] of Object.entries(itemMap)) { if (itemName === name) { baseItem = expandItem(itemObj); break; diff --git a/js/display.js b/js/display.js index 5235cbf..c89bf8a 100644 --- a/js/display.js +++ b/js/display.js @@ -1885,17 +1885,17 @@ function displayIDProbabilities(parent_id, item, amp) { if (amp != 0) {toggleButton("cork_amp_" + amp)} let item_name = item.get("displayName"); - console.log(itemMap.get(item_name)) + console.log(itemMap[item_name]) let table_elem = document.createElement("table"); parent_elem.appendChild(table_elem); - for (const [id,val] of Object.entries(itemMap.get(item_name))) { + for (const [id,val] of Object.entries(itemMap[item_name])) { if (rolledIDs.includes(id)) { let min = item.get("minRolls").get(id); let max = item.get("maxRolls").get(id); //Apply corkian amps if (val > 0) { - let base = itemMap.get(item_name)[id]; + let base = val; if (reversedIDs.includes(id)) {max = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)} else {min = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)} } diff --git a/js/item.js b/js/item.js index c29d2f0..3ab6dfd 100644 --- a/js/item.js +++ b/js/item.js @@ -17,9 +17,8 @@ let amp_state = 0; //the level of corkian map used for ID purposes. Default 0. function init_itempage() { //console.log(item_url_tag); - //displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view"); try{ - item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []); + item = expandItem(itemMap[item_url_tag.replaceAll("%20"," ")], []); displaysq2ExpandedItem(item, "item-view"); displaysq2AdditionalInfo("additional-info", item); displaysq2IDCosts("identification-costs", item); diff --git a/js/load.js b/js/load.js index eceed6d..2f957c0 100644 --- a/js/load.js +++ b/js/load.js @@ -7,8 +7,8 @@ let load_complete = false; let load_in_progress = false; let items; let sets = new Map(); -let itemMap; -let idMap; +let itemMap = {}; +let idMap = {}; let redirectMap; let itemLists = {}; // List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. @@ -232,26 +232,15 @@ for (let i = 0; i < none_items.length; i++) { } function init_maps() { - //warp - itemMap = new Map(); - /* Mapping from item names to set names. */ - idMap = new Map(); - redirectMap = new Map(); - items = items.concat(none_items); - //console.log(items); for (const item of items) { - if (item.remapID === undefined) { - itemLists[item.type].push(item.displayName); - itemMap.set(item.displayName, item); - if (none_items.includes(item)) { - idMap.set(item.id, ""); - } - else { - idMap.set(item.id, item.displayName); - } - } - else { - redirectMap.set(item.id, item.remapID); - } + itemLists[item.type].push(item.displayName); + itemMap[item.displayName] = item; + idMap[item.id] = item.displayName; } + for (const item of none_items) { + itemLists[item.type].push(item.displayName); + itemMap[item.displayName] = item; + idMap[item.id] = ""; + } + items = items.concat(none_items); } diff --git a/js/load_ing.js b/js/load_ing.js index e5d37f4..0e8e676 100644 --- a/js/load_ing.js +++ b/js/load_ing.js @@ -9,13 +9,13 @@ let iload_complete = false; let ings; let recipes; -let ingMap; +let ingMap = {}; let ingList = []; let recipeMap; let recipeList = []; -let ingIDMap; +let ingIDMap = {}; let recipeIDMap; /* @@ -163,39 +163,41 @@ async function load_ing_init() { } function init_ing_maps() { - ingMap = new Map(); recipeMap = new Map(); - ingIDMap = new Map(); recipeIDMap = new Map(); - let ing = Object(); - ing.name = "No Ingredient"; - ing.displayName = "No Ingredient"; - ing.tier = 0; - ing.lvl = 0; - ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"]; - ing.ids= {}; - ing.itemIDs = {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,}; - ing.consumableIDs = {"dura": 0, "charges": 0}; - ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}; - ing.id = 4000; - ingMap.set(ing["displayName"], ing); - ingList.push(ing["displayName"]); - ingIDMap.set(ing["id"], ing["displayName"]); + let ing = { + name: "No Ingredient", + displayName: "No Ingredient", + tier: 0, + lvl: 0, + skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"], + ids: {}, + itemIDs: {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,}, + consumableIDs: {"dura": 0, "charges": 0}, + posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}, + id: 4000 + }; + ingMap[ing.displayName] = ing; + ingList.push(ing.displayName); + ingIDMap[ing.id] = ing.displayName; let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]); for (let i = 0; i < 5; i ++) { for (const powderIng of powderIngreds) { - let ing = Object(); - ing.name = "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1); - ing.displayName = ing.name - ing.tier = 0; - ing.lvl = 0; - ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"]; - ing.ids = {}; - ing.isPowder = true; - ing.pid = 6*i + powderIngreds.indexOf(powderIng); + let ing = { + name: "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1), + tier: 0, + lvl: 0, + skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"], + ids: {}, + isPowder: true, + pid: 6*i + powderIngreds.indexOf(powderIng), + itemIDs: {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0}, + consumableIDs: {"dura": 0, "charges": 0}, + posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0} + }; ing.id = 4001 + ing.pid; - ing.itemIDs = {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,}; + ing.diplayName = ing.name; switch(i) { case 0: ing.itemIDs["strReq"] = powderIng["skpReq"]; @@ -213,23 +215,21 @@ function init_ing_maps() { ing.itemIDs["agiReq"] = powderIng["skpReq"]; break; } - ing.consumableIDs = {"dura": 0, "charges": 0}; - ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}; - ingMap.set(ing["displayName"],ing); - ingList.push(ing["displayName"]); - ingIDMap.set(ing["id"], ing["displayName"]); + ingMap[ing.displayName] = ing; + ingList.push(ing.displayName); + ingIDMap[ing.id] = ing.displayName; } } for (const ing of ings) { - ingMap.set(ing["displayName"], ing); - ingList.push(ing["displayName"]); - ingIDMap.set(ing["id"], ing["displayName"]); + ingMap[ing.displayName] = ing; + ingList.push(ing.displayName); + ingIDMap[ing.id] = ing.displayName; } for (const recipe of recipes) { - recipeMap.set(recipe["name"], recipe); - recipeList.push(recipe["name"]); - recipeIDMap.set(recipe["id"],recipe["name"]); + recipeMap.set(recipe.name, recipe); + recipeList.push(recipe.name); + recipeIDMap.set(recipe.id, recipe.name); } } diff --git a/js/skillpoints.js b/js/skillpoints.js index 86b130c..9ce7b7c 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -214,7 +214,7 @@ function calculate_skillpoints(equipment, weapon) { const end = Date.now(); const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`; console.log(output_msg); - document.getElementById('stack-box').textContent = output_msg; + document.getElementById('stack-box').textContent += output_msg; return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts]; } From 3e9414ef1eb6109357071324484a4805903a14ec Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 16:34:54 -0500 Subject: [PATCH 06/14] Revert Map changes for big maps... use only for small object-like things --- js/build_encode_decode.js | 2 +- js/builder.js | 18 ++++----------- js/builder_graph.js | 3 +-- js/computation_graph.js | 18 ++++++--------- js/craft.js | 2 +- js/crafter.js | 13 +++++++++-- js/customizer.js | 4 ++-- js/display.js | 6 ++--- js/item.js | 9 ++++---- js/load.js | 48 ++++++++++++++++++++++++--------------- js/load_ing.js | 16 ++++++------- js/skillpoints.js | 4 ++-- 12 files changed, 76 insertions(+), 67 deletions(-) diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index b4619f5..a5f88d0 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -1,7 +1,7 @@ let player_build; let build_powders; -function getItemNameFromID(id) { return idMap[id]; } +function getItemNameFromID(id) { return idMap.get(id); } function getTomeNameFromID(id) { return tomeIDMap.get(id); } function parsePowdering(powder_info) { diff --git a/js/builder.js b/js/builder.js index d5c7b32..215ef22 100644 --- a/js/builder.js +++ b/js/builder.js @@ -164,8 +164,8 @@ function init_autocomplete() { let item_arr = []; if (eq == 'weapon') { for (const weaponType of weapon_keys) { - for (const weapon of itemLists[weaponType]) { - let item_obj = itemMap[weapon]; + for (const weapon of itemLists.get(weaponType)) { + let item_obj = itemMap.get(weapon); if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { continue; } @@ -176,8 +176,8 @@ function init_autocomplete() { } } } else { - for (const item of itemLists[eq.replace(/[0-9]/g, '')]) { - let item_obj = itemMap[item]; + for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) { + let item_obj = itemMap.get(item); if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { continue; } @@ -220,7 +220,7 @@ function init_autocomplete() { class: "scaled-font search-item", selected: "dark-5", element: (item, data) => { - item.classList.add(itemMap[data.value].tier); + item.classList.add(itemMap.get(data.value).tier); }, }, events: { @@ -390,15 +390,7 @@ window.onerror = function(message, source, lineno, colno, error) { }; (async function() { - const start = Date.now(); let load_promises = [ load_init(), load_ing_init(), load_tome_init() ]; await Promise.all(load_promises); - const codestart = Date.now(); init(); - const end = Date.now(); - const calc_str = `builder calculation took ${(end-codestart)/ 1000} seconds.`; - const total_str = `builder total took ${(end-start)/ 1000} seconds.`; - console.log(calc_str); - console.log(total_str); - document.getElementById('stack-box').textContent += calc_str + total_str; })(); diff --git a/js/builder_graph.js b/js/builder_graph.js index db7c231..f836986 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -154,7 +154,7 @@ class ItemInputNode extends InputNode { let item; if (item_text.slice(0, 3) == "CI-") { item = getCustomFromHash(item_text); } else if (item_text.slice(0, 3) == "CR-") { item = getCraftFromHash(item_text); } - else if (itemMap[item_text]) { item = new Item(itemMap[item_text]); } + else if (itemMap.has(item_text)) { item = new Item(itemMap.get(item_text)); } else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); } if (item) { @@ -1143,6 +1143,5 @@ function builder_graph_init() { // this will propagate the update to the `stat_agg_node`, and then to damage calc console.log("Set up graph"); - INPUT_UPDATE = true; } diff --git a/js/computation_graph.js b/js/computation_graph.js index d32554d..1f13851 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -174,24 +174,20 @@ class ValueCheckComputeNode extends ComputeNode { } -let INPUT_UPDATE = false; - /** * Schedule a ComputeNode to be updated. * * @param node : ComputeNode to schedule an update for. */ function calcSchedule(node, timeout) { - if (INPUT_UPDATE) { - if (node.update_task !== null) { - clearTimeout(node.update_task); - } - node.mark_dirty(); - node.update_task = setTimeout(function() { - node.update(); - node.update_task = null; - }, timeout); + if (node.update_task !== null) { + clearTimeout(node.update_task); } + node.mark_dirty(); + node.update_task = setTimeout(function() { + node.update(); + node.update_task = null; + }, timeout); } class PrintNode extends ComputeNode { diff --git a/js/craft.js b/js/craft.js index 59c1446..db30aef 100644 --- a/js/craft.js +++ b/js/craft.js @@ -34,7 +34,7 @@ function getCraftFromHash(hash) { if (version === "1") { let ingreds = []; for (let i = 0; i < 6; i ++ ) { - ingreds.push( expandIngredient(ingMap[ingIDMap[Base64.toInt(name.substring(2*i,2*i+2))]]) ); + ingreds.push( expandIngredient(ingMap.get(ingIDMap.get(Base64.toInt(name.substring(2*i,2*i+2))))) ); } let recipe = expandRecipe(recipeMap.get(recipeIDMap.get(Base64.toInt(name.substring(12,14))))); diff --git a/js/crafter.js b/js/crafter.js index f4f461b..865b740 100644 --- a/js/crafter.js +++ b/js/crafter.js @@ -32,6 +32,15 @@ let player_craft; function init_crafter() { //no ing + + console.log("all ingredients"); + console.log(ingMap); + console.log("all recipes"); + console.log(recipeMap); + /*console.log(ingList); + console.log(recipeList); + console.log(ingIDMap); + console.log(recipeIDMap);*/ try { document.getElementById("recipe-choice").addEventListener("change", (event) => { updateMaterials(); @@ -140,7 +149,7 @@ function calculateCraft() { for (i = 1; i < 7; i++) { console.log("ing-choice-"+i); // console.log(getValue("ing-choice-"+i)); - getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap["No Ingredient"])) : ingreds.push(expandIngredient(ingMap[getValue("ing-choice-" + i)])); + getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i)))); } let atkSpd = "NORMAL"; //default attack speed will be normal. for (const b of ["slow-atk-button", "normal-atk-button", "fast-atk-button"]) { @@ -205,7 +214,7 @@ function decodeCraft(ing_url_tag) { if (version === "1") { ingreds = []; for (let i = 0; i < 6; i ++ ) { - setValue("ing-choice-"+(i+1), ingIDMap[Base64.toInt(tag.substring(2*i,2*i+2))]); + setValue("ing-choice-"+(i+1), ingIDMap.get(Base64.toInt(tag.substring(2*i,2*i+2)))); //console.log(Base64.toInt(tag.substring(2*i,2*i+2))); } recipe = recipeIDMap.get(Base64.toInt(tag.substring(12,14))); diff --git a/js/customizer.js b/js/customizer.js index a916bca..059a849 100644 --- a/js/customizer.js +++ b/js/customizer.js @@ -331,7 +331,7 @@ function populateFields() { class_list.appendChild(el); } let item_list = document.getElementById("base-list"); - for (const name of Object.keys(itemMap)) { + for (const name of itemMap.keys()) { let el = document.createElement("option"); el.value = name; item_list.appendChild(el); @@ -368,7 +368,7 @@ function useBaseItem(elem) { let baseItem; //Check items db. - for (const [name,itemObj] of Object.entries(itemMap)) { + for (const [name,itemObj] of itemMap) { if (itemName === name) { baseItem = expandItem(itemObj); break; diff --git a/js/display.js b/js/display.js index 0633063..24813a9 100644 --- a/js/display.js +++ b/js/display.js @@ -1871,18 +1871,18 @@ function displayIDProbabilities(parent_id, item, amp) { if (amp != 0) {toggleButton("cork_amp_" + amp)} let item_name = item.get("displayName"); - console.log(itemMap[item_name]) + console.log(itemMap.get(item_name)) let table_elem = document.createElement("table"); parent_elem.appendChild(table_elem); - for (const [id,val] of Object.entries(itemMap[item_name])) { + for (const [id,val] of Object.entries(itemMap.get(item_name))) { if (rolledIDs.includes(id)) { if (!item.get("maxRolls").get(id)) { continue; } let min = item.get("minRolls").get(id); let max = item.get("maxRolls").get(id); //Apply corkian amps if (val > 0) { - let base = val; + let base = itemMap.get(item_name)[id]; if (reversedIDs.includes(id)) {max = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)} else {min = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)} } diff --git a/js/item.js b/js/item.js index d2734cb..4371afe 100644 --- a/js/item.js +++ b/js/item.js @@ -17,15 +17,16 @@ let amp_state = 0; //the level of corkian map used for ID purposes. Default 0. function init_itempage() { //console.log(item_url_tag); + //displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view"); try{ - item = expandItem(itemMap[item_url_tag.replaceAll("%20"," ")], []); + item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []); if (item.get('category') === 'weapon') { item.set('powders', []); apply_weapon_powders(item); } - displaysq2ExpandedItem(item, "item-view"); - displaysq2AdditionalInfo("additional-info", item); - displaysq2IDCosts("identification-costs", item); + displayExpandedItem(item, "item-view"); + displayAdditionalInfo("additional-info", item); + displayIDCosts("identification-costs", item); if (item.get("set") && sets[item.get("set")]) { displayAllSetBonuses("set-bonus-info",item.get("set")); } diff --git a/js/load.js b/js/load.js index 2f957c0..c425c97 100644 --- a/js/load.js +++ b/js/load.js @@ -7,15 +7,10 @@ let load_complete = false; let load_in_progress = false; let items; let sets = new Map(); -let itemMap = {}; -let idMap = {}; +let itemMap; +let idMap; let redirectMap; -let itemLists = {}; -// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. -for (const it of itemTypes) { - itemLists[it] = []; -} - +let itemLists = new Map(); /* * Load item set from local DB. Calls init() on success. */ @@ -194,6 +189,11 @@ async function load_init() { }); } +// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. +for (const it of itemTypes) { + itemLists.set(it, []); +} + let none_items = [ ["armor", "helmet", "No Helmet"], ["armor", "chestplate", "No Chestplate"], @@ -232,15 +232,27 @@ for (let i = 0; i < none_items.length; i++) { } function init_maps() { - for (const item of items) { - itemLists[item.type].push(item.displayName); - itemMap[item.displayName] = item; - idMap[item.id] = item.displayName; - } - for (const item of none_items) { - itemLists[item.type].push(item.displayName); - itemMap[item.displayName] = item; - idMap[item.id] = ""; - } + //warp + itemMap = new Map(); + /* Mapping from item names to set names. */ + idMap = new Map(); + redirectMap = new Map(); items = items.concat(none_items); + //console.log(items); + for (const item of items) { + if (item.remapID === undefined) { + itemLists.get(item.type).push(item.displayName); + itemMap.set(item.displayName, item); + if (none_items.includes(item)) { + idMap.set(item.id, ""); + } + else { + idMap.set(item.id, item.displayName); + } + } + else { + redirectMap.set(item.id, item.remapID); + } + } + console.log(itemMap); } diff --git a/js/load_ing.js b/js/load_ing.js index 0e8e676..7652efb 100644 --- a/js/load_ing.js +++ b/js/load_ing.js @@ -9,13 +9,13 @@ let iload_complete = false; let ings; let recipes; -let ingMap = {}; +let ingMap = new Map(); let ingList = []; let recipeMap; let recipeList = []; -let ingIDMap = {}; +let ingIDMap = new Map(); let recipeIDMap; /* @@ -178,9 +178,9 @@ function init_ing_maps() { posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}, id: 4000 }; - ingMap[ing.displayName] = ing; + ingMap.set(ing.displayName, ing); ingList.push(ing.displayName); - ingIDMap[ing.id] = ing.displayName; + ingIDMap.set(ing.id, ing.displayName); let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]); for (let i = 0; i < 5; i ++) { for (const powderIng of powderIngreds) { @@ -215,17 +215,17 @@ function init_ing_maps() { ing.itemIDs["agiReq"] = powderIng["skpReq"]; break; } - ingMap[ing.displayName] = ing; + ingMap.set(ing.displayName, ing); ingList.push(ing.displayName); - ingIDMap[ing.id] = ing.displayName; + ingIDMap.set(ing.id, ing.displayName); } } for (const ing of ings) { - ingMap[ing.displayName] = ing; + ingMap.set(ing.displayName, ing); ingList.push(ing.displayName); - ingIDMap[ing.id] = ing.displayName; + ingIDMap.set(ing.id, ing.displayName); } for (const recipe of recipes) { recipeMap.set(recipe.name, recipe); diff --git a/js/skillpoints.js b/js/skillpoints.js index 9ce7b7c..4faceff 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -1,5 +1,5 @@ function calculate_skillpoints(equipment, weapon) { - const start = Date.now(); + const start = performance.now(); // Calculate equipment equipping order and required skillpoints. // Return value: [equip_order, best_skillpoints, final_skillpoints, best_total]; let fixed = []; @@ -211,7 +211,7 @@ function calculate_skillpoints(equipment, weapon) { // best_skillpoints: manually assigned (before any gear) // final_skillpoints: final totals (5 individ) // best_total: total skillpoints assigned (number) - const end = Date.now(); + const end = performance.now(); const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`; console.log(output_msg); document.getElementById('stack-box').textContent += output_msg; From 1e4ebee975032530579a09482de530d21695d347 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 20:44:00 -0700 Subject: [PATCH 07/14] First iteration blazing fast sp engine --- js/build.js | 1 - js/builder_graph.js | 5 +- js/skillpoints.js | 351 +++++++++++++++++++++++++------------------- 3 files changed, 201 insertions(+), 156 deletions(-) diff --git a/js/build.js b/js/build.js index fd3dd82..f079909 100644 --- a/js/build.js +++ b/js/build.js @@ -30,7 +30,6 @@ class Build{ this.level = level; } else if (typeof level === "string") { this.level = level; - errors.push(new IncorrectInput(level, "a number", "level-choice")); } else { errors.push("Level is not a string or number."); } diff --git a/js/builder_graph.js b/js/builder_graph.js index f836986..ffe2d08 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -407,7 +407,10 @@ class BuildAssembleNode extends ComputeNode { input_map.get('guildTome1-input') ]; let weapon = input_map.get('weapon-input'); - let level = input_map.get('level-input'); + let level = parseInt(input_map.get('level-input')); + if (isNaN(level)) { + level = 106; + } let all_none = weapon.statMap.has('NONE'); for (const item of equipments) { diff --git a/js/skillpoints.js b/js/skillpoints.js index 4faceff..6a2ee98 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -1,3 +1,82 @@ +/** + * Apply skillpoint bonuses from an item. + * Also applies set deltas. + * Modifies the skillpoints array. + */ +function apply_skillpoints(skillpoints, item, activeSetCounts) { + for (let i = 0; i < 5; i++) { + skillpoints[i] += item.skillpoints[i]; + } + + const setName = item.set; + if (setName) { // undefined/null means no set. + let setCount = activeSetCounts.get(setName); + let old_bonus = {}; + if (setCount) { + old_bonus = sets.get(setName).bonuses[setCount-1]; + activeSetCounts.set(setName, setCount + 1); + } + else { + setCount = 0; + activeSetCounts.set(setName, 1); + } + const new_bonus = sets.get(setName).bonuses[setCount]; + //let skp_order = ["str","dex","int","def","agi"]; + for (const i in skp_order) { + const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0); + skillpoints[i] += delta; + } + } +} + +/** + * Apply skillpoints until this item can be worn. + * Also applies set deltas. + * Confusingly, does not modify the skillpoints array. + * Instead, return an array of deltas. + */ +function apply_to_fit(skillpoints, item, skillpoint_min, activeSetCounts) { + let applied = [0, 0, 0, 0, 0]; + for (let i = 0; i < 5; i++) { + if (item.skillpoints[i] < 0 && skillpoint_min[i]) { + const unadjusted = skillpoints[i] + item.skillpoints[i]; + const delta = skillpoint_min[i] - unadjusted; + if (delta > 0) { + applied[i] += delta; + } + } + if (item.reqs[i] == 0) continue; + skillpoint_min[i] = Math.max(skillpoint_min[i], item.reqs[i] + item.skillpoints[i]); + const req = item.reqs[i]; + const cur = skillpoints[i]; + if (req > cur) { + const diff = req - cur; + applied[i] += diff; + } + } + + const setName = item.set; + if (setName) { // undefined/null means no set. + const setCount = activeSetCounts.get(setName); + if (setCount) { + const old_bonus = sets.get(setName).bonuses[setCount-1]; + const new_bonus = sets.get(setName).bonuses[setCount]; + //let skp_order = ["str","dex","int","def","agi"]; + for (const i in skp_order) { + const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0); + if (set_delta < 0 && skillpoint_min[i]) { + const unadjusted = skillpoints[i] + set_delta; + const delta = skillpoint_min[i] - unadjusted; + if (delta > 0) { + applied[i] += delta; + } + } + } + } + } + return applied; +} + function calculate_skillpoints(equipment, weapon) { const start = performance.now(); // Calculate equipment equipping order and required skillpoints. @@ -28,78 +107,7 @@ function calculate_skillpoints(equipment, weapon) { consider.push(item); } } - function apply_skillpoints(skillpoints, item, activeSetCounts) { - for (let i = 0; i < 5; i++) { - skillpoints[i] += item.skillpoints[i]; - } - const setName = item.set; - if (setName) { // undefined/null means no set. - let setCount = activeSetCounts.get(setName); - let old_bonus = {}; - if (setCount) { - old_bonus = sets.get(setName).bonuses[setCount-1]; - activeSetCounts.set(setName, setCount + 1); - } - else { - setCount = 0; - activeSetCounts.set(setName, 1); - } - const new_bonus = sets.get(setName).bonuses[setCount]; - //let skp_order = ["str","dex","int","def","agi"]; - for (const i in skp_order) { - const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0); - skillpoints[i] += delta; - } - } - } - - function apply_to_fit(skillpoints, item, skillpoint_min, activeSetCounts) { - let applied = [0, 0, 0, 0, 0]; - let total = 0; - for (let i = 0; i < 5; i++) { - if (item.skillpoints[i] < 0 && skillpoint_min[i]) { - const unadjusted = skillpoints[i] + item.skillpoints[i]; - const delta = skillpoint_min[i] - unadjusted; - if (delta > 0) { - applied[i] += delta; - total += delta; - } - } - if (item.reqs[i] == 0) continue; - skillpoint_min[i] = Math.max(skillpoint_min[i], item.reqs[i] + item.skillpoints[i]); - const req = item.reqs[i]; - const cur = skillpoints[i]; - if (req > cur) { - const diff = req - cur; - applied[i] += diff; - total += diff; - } - } - - const setName = item.set; - if (setName) { // undefined/null means no set. - const setCount = activeSetCounts.get(setName); - if (setCount) { - const old_bonus = sets.get(setName).bonuses[setCount-1]; - const new_bonus = sets.get(setName).bonuses[setCount]; - //let skp_order = ["str","dex","int","def","agi"]; - for (const i in skp_order) { - const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0); - if (set_delta < 0 && skillpoint_min[i]) { - const unadjusted = skillpoints[i] + set_delta; - const delta = skillpoint_min[i] - unadjusted; - if (delta > 0) { - applied[i] += delta; - total += delta; - } - } - } - } - } - - return [applied, total]; - } // Separate out the no req items and add them to the static skillpoint base. let static_skillpoints_base = [0, 0, 0, 0, 0] @@ -108,7 +116,7 @@ function calculate_skillpoints(equipment, weapon) { apply_skillpoints(static_skillpoints_base, item, static_activeSetCounts); } - let best = consider.concat(noboost); + let best = consider; let final_skillpoints = static_skillpoints_base.slice(); let best_skillpoints = [0, 0, 0, 0, 0]; let best_total = Infinity; @@ -117,97 +125,91 @@ function calculate_skillpoints(equipment, weapon) { let allFalse = [0, 0, 0, 0, 0]; if (consider.length > 0 || noboost.length > 0 || crafted.length > 0) { // Try every combination and pick the best one. - construct_scc_graph(consider); - for (let permutation of perm(consider)) { - let activeSetCounts = new Map(static_activeSetCounts); - let has_skillpoint = allFalse.slice(); - permutation = permutation.concat(noboost); + const [root, terminal, sccs] = construct_scc_graph(consider); + console.log(sccs); + const end_checks = crafted.concat(noboost); + end_checks.push(weapon); + // naive way first. - let skillpoints_applied = [0, 0, 0, 0, 0]; - // Complete slice is a shallow copy. - let skillpoints = static_skillpoints_base.slice(); - - let total_applied = 0; - - let result; - let needed_skillpoints; - let total_diff; - for (const item of permutation) { - result = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts); - needed_skillpoints = result[0]; - total_diff = result[1]; - - for (let i = 0; i < 5; ++i) { - skillpoints_applied[i] += needed_skillpoints[i]; - skillpoints[i] += needed_skillpoints[i]; - } - apply_skillpoints(skillpoints, item, activeSetCounts); - total_applied += total_diff; - if (total_applied >= best_total) { - break; - } - } - + function check_end(skillpoints_applied, skillpoints, activeSetCounts, total_applied) { // Crafted skillpoint does not count initially. - for (const item of crafted) { - //console.log(item) - result = apply_to_fit(skillpoints, item, allFalse.slice(), activeSetCounts); - //console.log(result) - needed_skillpoints = result[0]; - total_diff = result[1]; + for (const item of end_checks) { + const needed_skillpoints = apply_to_fit(skillpoints, item, + [false, false, false, false, false], activeSetCounts); for (let i = 0; i < 5; ++i) { - skillpoints_applied[i] += needed_skillpoints[i]; - skillpoints[i] += needed_skillpoints[i]; + const skp = needed_skillpoints[i] + skillpoints_applied[i] += skp; + skillpoints[i] += skp; + total_applied += skp; } - total_applied += total_diff; - } - - if (total_applied >= best_total) { - continue; - } - - let pre = skillpoints.slice(); - result = apply_to_fit(skillpoints, weapon, allFalse.slice(), activeSetCounts); - needed_skillpoints = result[0]; - total_diff = result[1]; - for (let i = 0; i < 5; ++i) { - skillpoints_applied[i] += needed_skillpoints[i]; - skillpoints[i] += needed_skillpoints[i]; - } - - apply_skillpoints(skillpoints, weapon, activeSetCounts); - total_applied += total_diff; - - // Applying crafted item skill points last. - for (const item of crafted) { - apply_skillpoints(skillpoints, item, activeSetCounts); - //total_applied += total_diff; - } - - if (total_applied < best_total) { - best = permutation; - final_skillpoints = skillpoints; - best_skillpoints = skillpoints_applied; - best_total = total_applied; - best_activeSetCounts = activeSetCounts; + if (best_total < total_applied) { return -1; } } + return total_applied; } + function permute_check(idx, _applied, _skillpoints, _sets, _has, _total_applied, order) { + const {nodes, children} = sccs[idx]; + if (nodes[0] === terminal) { + const total = check_end(_applied, _skillpoints, _sets, _total_applied); + if (total < best_total) { + final_skillpoints = _skillpoints; + best_skillpoints = _applied; + best_total = total; + best_activeSetCounts = _sets; + best = order; + console.log('new best'); + console.log(order); + } + return; + } + for (let permutation of perm(nodes)) { + const skillpoints_applied = _applied.slice(); + const skillpoints = _skillpoints.slice(); + const activeSetCounts = new Map(_sets); + const has_skillpoint = _has.slice(); + let total_applied = _total_applied; + let short_circuit = false; + for (const {item} of permutation) { + needed_skillpoints = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts); + for (let i = 0; i < 5; ++i) { + skp = needed_skillpoints[i]; + skillpoints_applied[i] += skp; + skillpoints[i] += skp; + total_applied += skp; + } + if (total_applied >= best_total) { + short_circuit = true; + break; // short circuit failure + } + apply_skillpoints(skillpoints, item, activeSetCounts); + } + if (short_circuit) { continue; } + permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item))); + } + } + // skip root. + permute_check(1, final_skillpoints, best_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []); + + // add extra sp bonus + apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts); + // Applying crafted item skill points last. + for (const item of crafted) { + apply_skillpoints(final_skillpoints, item, best_activeSetCounts); + } } else { best_total = 0; - result = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts); - needed_skillpoints = result[0]; - total_diff = result[1]; + needed_skillpoints = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts); for (let i = 0; i < 5; ++i) { - best_skillpoints[i] += needed_skillpoints[i]; - final_skillpoints[i] += needed_skillpoints[i]; + const skp = needed_skillpoints[i]; + best_skillpoints[i] += skp; + final_skillpoints[i] += skp; + best_skillpoints += skp; } apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts); - best_total += total_diff; } - let equip_order = fixed.concat(best).concat(crafted); + let equip_order = fixed.concat(best).concat(noboost).concat(crafted); // best_skillpoints: manually assigned (before any gear) // final_skillpoints: final totals (5 individ) // best_total: total skillpoints assigned (number) @@ -220,21 +222,32 @@ function calculate_skillpoints(equipment, weapon) { function construct_scc_graph(items_to_consider) { let nodes = []; - for (const item of items_to_consider) { - nodes.push({item: item, children: [], parents: [], visited: false}); - } + let terminal_node = { + item: null, + children: [], + parents: nodes, + visited: false, + assigned: false, + scc: null + }; let root_node = { + item: null, children: nodes, parents: [], - visited: false + visited: false, + assigned: false, + scc: null }; + for (const item of items_to_consider) { + nodes.push({item: item, children: [terminal_node], parents: [root_node], visited: false, assigned: false, scc: null}); + } // Dependency graph construction. for (const node_a of nodes) { const {item: a, children: a_children} = node_a; for (const node_b of nodes) { const {item: b, parents: b_parents} = node_b; for (let i = 0; i < 5; ++i) { - if (b.reqs[i] < a.reqs[i] && b.skillpoints[i]) { + if (a.reqs[i] < b.reqs[i] && a.skillpoints[i]) { a_children.push(node_b); b_parents.push(node_a); break; @@ -257,5 +270,35 @@ function construct_scc_graph(items_to_consider) { } visit(root_node, res); res.reverse(); - console.log(res); + const sccs = []; + function assign(node, cur_scc) { + if (node.assigned) { return; } + cur_scc.nodes.push(node); + node.scc = cur_scc; + node.assigned = true; + for (const parent of node.parents) { + assign(parent, cur_scc); + } + } + for (const node of res) { + if (node.assigned) { continue; } + const cur_scc = { + nodes: [], + children: new Set(), + parents: new Set() + }; + assign(node, cur_scc); + sccs.push(cur_scc); + } + for (const scc of sccs) { + for (const node of scc.nodes) { + for (const child of node.children) { + scc.children.add(child.scc); + } + for (const parent of node.parents) { + scc.parents.add(parent.scc); + } + } + } + return [root_node, terminal_node, sccs]; } From 0a508ea0aed2f060152e6613db8bcc993fea9daa Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 22:53:24 -0500 Subject: [PATCH 08/14] Fix typo with skillpoint engine initialization --- js/skillpoints.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index 6a2ee98..caca6a6 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -189,7 +189,7 @@ function calculate_skillpoints(equipment, weapon) { } } // skip root. - permute_check(1, final_skillpoints, best_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []); + permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []); // add extra sp bonus apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts); From 338e1931bc88d2b20b36ae2f9ea7f7631847aa3f Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 23:00:27 -0500 Subject: [PATCH 09/14] -sp case --- js/skillpoints.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index caca6a6..24f8b3a 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -247,7 +247,7 @@ function construct_scc_graph(items_to_consider) { for (const node_b of nodes) { const {item: b, parents: b_parents} = node_b; for (let i = 0; i < 5; ++i) { - if (a.reqs[i] < b.reqs[i] && a.skillpoints[i]) { + if (a.skillpoints[i] > 0 && (a.reqs[i] < b.reqs[i] || b.skillpoints[i] < 0)) { a_children.push(node_b); b_parents.push(node_a); break; From 5daa7064cd6e9510660fab462b71d99a5422736c Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 23:13:20 -0500 Subject: [PATCH 10/14] Separate cases a bit more cleanly, include -sp items in noboost --- js/skillpoints.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index 24f8b3a..d739c5d 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -95,12 +95,15 @@ function calculate_skillpoints(equipment, weapon) { if (item.get("crafted")) { crafted.push(item); } + // TODO hack: We will treat ALL set items as unsafe :( + else if (item.set !== null) { + consider.push(item); + } else if (item.get("reqs").every(x => x === 0) && item.skillpoints.every(x => x >= 0)) { // All reqless item without -skillpoints. fixed.push(item); } - // TODO hack: We will treat ALL set items as unsafe :( - else if (item.skillpoints.every(x => x === 0) && item.set === null) { + else if (item.skillpoints.every(x => x <= 0)) { noboost.push(item); } else { From d4706ebb7488a074c9d8a8df9139fa5e5c521e21 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 22:04:38 -0700 Subject: [PATCH 11/14] Remove debugging msg for skillpoint calc --- js/skillpoints.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index d739c5d..a13fd88 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -219,7 +219,6 @@ function calculate_skillpoints(equipment, weapon) { const end = performance.now(); const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`; console.log(output_msg); - document.getElementById('stack-box').textContent += output_msg; return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts]; } From 24a7dd9592e525758f6dcfa00e58a720cdc7d793 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 22:39:30 -0700 Subject: [PATCH 12/14] Clean up, remove prints --- js/skillpoints.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index a13fd88..6e20409 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -129,10 +129,8 @@ function calculate_skillpoints(equipment, weapon) { if (consider.length > 0 || noboost.length > 0 || crafted.length > 0) { // Try every combination and pick the best one. const [root, terminal, sccs] = construct_scc_graph(consider); - console.log(sccs); const end_checks = crafted.concat(noboost); end_checks.push(weapon); - // naive way first. function check_end(skillpoints_applied, skillpoints, activeSetCounts, total_applied) { // Crafted skillpoint does not count initially. From 2d9a6bb5a172c92609a82ccdd88038e8f49351fb Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 23:10:16 -0700 Subject: [PATCH 13/14] Fix check for.. the guard that I put in myself... --- js/skillpoints.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index 6e20409..18d6ef5 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -131,6 +131,7 @@ function calculate_skillpoints(equipment, weapon) { const [root, terminal, sccs] = construct_scc_graph(consider); const end_checks = crafted.concat(noboost); end_checks.push(weapon); + console.log(sccs); function check_end(skillpoints_applied, skillpoints, activeSetCounts, total_applied) { // Crafted skillpoint does not count initially. @@ -152,15 +153,16 @@ function calculate_skillpoints(equipment, weapon) { function permute_check(idx, _applied, _skillpoints, _sets, _has, _total_applied, order) { const {nodes, children} = sccs[idx]; if (nodes[0] === terminal) { + console.log(order); const total = check_end(_applied, _skillpoints, _sets, _total_applied); - if (total < best_total) { + if (total !== -1 && total < best_total) { final_skillpoints = _skillpoints; best_skillpoints = _applied; best_total = total; best_activeSetCounts = _sets; best = order; console.log('new best'); - console.log(order); + console.log(total); } return; } From 6e590dde012aeb808860c63c0ef470bc90a94f04 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 17 Jul 2022 23:33:39 -0700 Subject: [PATCH 14/14] Remove debugging print (2) --- js/skillpoints.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/js/skillpoints.js b/js/skillpoints.js index 18d6ef5..f2fa858 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -78,7 +78,7 @@ function apply_to_fit(skillpoints, item, skillpoint_min, activeSetCounts) { } function calculate_skillpoints(equipment, weapon) { - const start = performance.now(); + // const start = performance.now(); // Calculate equipment equipping order and required skillpoints. // Return value: [equip_order, best_skillpoints, final_skillpoints, best_total]; let fixed = []; @@ -131,7 +131,6 @@ function calculate_skillpoints(equipment, weapon) { const [root, terminal, sccs] = construct_scc_graph(consider); const end_checks = crafted.concat(noboost); end_checks.push(weapon); - console.log(sccs); function check_end(skillpoints_applied, skillpoints, activeSetCounts, total_applied) { // Crafted skillpoint does not count initially. @@ -153,7 +152,6 @@ function calculate_skillpoints(equipment, weapon) { function permute_check(idx, _applied, _skillpoints, _sets, _has, _total_applied, order) { const {nodes, children} = sccs[idx]; if (nodes[0] === terminal) { - console.log(order); const total = check_end(_applied, _skillpoints, _sets, _total_applied); if (total !== -1 && total < best_total) { final_skillpoints = _skillpoints; @@ -161,8 +159,6 @@ function calculate_skillpoints(equipment, weapon) { best_total = total; best_activeSetCounts = _sets; best = order; - console.log('new best'); - console.log(total); } return; } @@ -216,9 +212,9 @@ function calculate_skillpoints(equipment, weapon) { // best_skillpoints: manually assigned (before any gear) // final_skillpoints: final totals (5 individ) // best_total: total skillpoints assigned (number) - const end = performance.now(); - const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`; - console.log(output_msg); + // const end = performance.now(); + // const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`; + // console.log(output_msg); return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts]; }