wynnbuilder-forked-for-changes/py_script/plot_dps.py
2021-06-22 07:47:39 -07:00

188 lines
7.8 KiB
Python

import matplotlib.pyplot as plt
import json
import numpy as np
#baselines_known_x = np.array([ 70, 72, 74, 75, 76, 78, 80, 82, 84, 85, 86, 89, 90, 91, 93, 95, 97, 98, 100 ])
#baselines_known_y = np.array([ 341.53, 358.29, 374.68, 383.35, 394.06, 410.75, 432.14, 453.45, 469.94, 480.93, 491.21, 524.26, 536.69, 546.26, 569.78, 592.45, 615.65, 626.75, 648.21 ]) / 2.05
item_type = "bow"
items_file = "../clean.json"
items_new_file = "../clean3.json"
min_level = 70
baselines_known_x = np.array([ 70, 75, 80, 85, 90, 95, 100 ])
baselines_known_y = np.array([ 341.53, 383.35, 432.14, 480.93, 536.69, 592.45, 648.21 ]) / 2.05
baselines_known_y_new = np.zeros(len(baselines_known_x))
for i, (x, y) in enumerate(zip(baselines_known_x, baselines_known_y)):
baselines_known_y_new[i] = y * (1 - 0.01 * (x - 70))
def interpolate_baseline(level):
i = 0
while baselines_known_x[i] <= level:
if baselines_known_x[i] == level:
return baselines_known_y[i]
i += 1
if i == len(baselines_known_x):
return baselines_known_y[-1]
start = i - 1
slope = ((baselines_known_y[i] - baselines_known_y[start])
/ (baselines_known_x[i] - baselines_known_x[start]))
dx = level - baselines_known_x[start]
return baselines_known_y[start] + slope * dx
reqs = ["dexReq", "strReq", "defReq", "agiReq", "intReq"]
# tefaw
powders_new = [ [4.5, 6.5, 8.5, 9, 10.5, 12.5, "T"],
[4.5, 6.5, 8, 8.5, 10, 12, "E"],
[3.5, 6, 7, 7.5, 9, 11, "F"],
[4, 6.5, 7.5, 8, 9.5, 11, "A"],
[3.5, 5, 6.5, 7, 8.5, 10, "W"]]
powders_old = [ [4.5, 6.5, 8.5, 9, 10.5, 22.5, "T"], # LOL only updating the 6th col
[4.5, 6.5, 8, 8.5, 10, 20, "E"],
[3.5, 6, 7, 7.5, 9, 17, "F"],
[4, 6.5, 7.5, 8, 9.5, 17, "A"],
[3.5, 5, 6.5, 7, 8.5, 15, "W"]]
def get_appropriate_powder_idx(item):
for i, req in enumerate(reqs):
if req in item and item[req] > 0:
return i
return 0
def get_display_name(item):
if "displayName" in item:
return item["displayName"]
return item["name"]
item_data = json.load(open(items_file))["items"]
item_map = {get_display_name(item): item for item in item_data}
item_new_data = json.load(open(items_new_file))["items"]
item_new_map = {get_display_name(item): item for item in item_new_data}
attack_speed_mods = {"SUPER_SLOW": 0.51, "VERY_SLOW": 0.83, "SLOW": 1.5, "NORMAL": 2.05, "FAST": 2.5, "VERY_FAST": 3.1, "SUPER_FAST": 4.3}
attack_speed_target_mult = {"SUPER_SLOW": 4, "VERY_SLOW": 2.5, "SLOW": 1.4, "NORMAL": 1, "FAST": 0.8, "VERY_FAST": 0.66, "SUPER_FAST": 0.48}
dps_to_baseline = dict()
min_mult = 10
max_mult = 0
for k in attack_speed_mods:
mult = attack_speed_mods[k] * attack_speed_target_mult[k]
if mult < min_mult:
min_mult = mult
if mult > max_mult:
max_mult = mult
dps_to_baseline[k] = 1/mult
weapon_type_mods = {"wand": 0.6, "spear": 0.8, "dagger": 1.0, "bow": 1.2, "relik": 1.2}
print((min_mult, max_mult))
tiers_mod = {"Normal": 0.8, "Unique": 1.0, "Rare": 1.1, "Legendary": 1.3, "Fabled": 1.5, "Mythic": 1.7, "Set": 1.05}
tiers_colors = {"Normal": (0.9, 0.9, 0.9), "Unique": (1, 1, 1/3), "Rare": (1, 1/3, 1), "Legendary": (1/3, 1, 1), "Fabled": (1, 1/3, 1/3), "Mythic": (2/3, 0, 2/3), "Set": (1/3, 1, 1/3)}
damage_types = ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]
# tefaw
damage_baseline_modifiers = [0.05, 0.05, -0.05, 0, -0.05]
def guess_design_modifier(item, base_dps):
level = item["lvl"]
tier = item["tier"]
nominal_baseline = interpolate_baseline(level) * weapon_type_mods[item["type"]] * tiers_mod[tier]
explanation = []
num_reqs = 0
total_modifier = 0
for i, req in enumerate(reqs):
if req in item and item[req] > 0:
num_reqs += 1
total_modifier += damage_baseline_modifiers[i]
explanation.append((req, damage_baseline_modifiers[i]))
num_damage_types = 0
for damage_type in damage_types[1:]:
if damage_type in item:
damages = item[damage_type].split("-")
if int(damages[1]) != 0:
num_damage_types += 1
is_rainbow = num_damage_types == 5 or num_reqs == 5
if is_rainbow:
total_modifier = 0.15
explanation = [("rainbow", 0.15)]
elif num_reqs > 1 or num_damage_types > 1:
total_modifier += 0.05
explanation.append(("multi_element", 0.05))
#total_modifier = 0.05
#explanation = [("multi_element", 0.05)]
nslots = 0
if "slots" in item:
nslots = item["slots"]
if nslots == 0:
total_modifier += 0.1
explanation.append(("zero_slot", 0.1))
elif nslots == 1:
total_modifier += 0.05
explanation.append(("one_slot", 0.05))
item_baseline = dps_to_baseline[item["atkSpd"]] * base_dps
actual_modifier = (item_baseline - nominal_baseline) / nominal_baseline
explained_baseline = nominal_baseline * (1 + total_modifier)
delta = (item_baseline - explained_baseline) / nominal_baseline
if delta >= 0.04 and delta < 0.075:
total_modifier += 0.05
explanation.append(("offensive", 0.05))
elif delta >= 0.075:
total_modifier += 0.1
explanation.append(("hyper_offensive", 0.1))
elif delta <= -0.04 and delta > -0.075:
total_modifier -= 0.05
explanation.append(("defensive", -0.05))
elif delta <= -0.075:
total_modifier -= 0.1
explanation.append(("hyper_defensive", -0.1))
if abs(delta) > 0.2:
print("LARGE BASELINE ERROR FOR ITEM " + get_display_name(item))
return total_modifier, actual_modifier, explanation
def get_data(item, powders):
total_damage = 0
for damage_type in damage_types:
if damage_type in item:
damages = item[damage_type].split("-")
total_damage += int(damages[0]) + int(damages[1])
total_damage /= 2
attack_speed_mod = attack_speed_mods[item["atkSpd"]]
dps = total_damage * attack_speed_mod
postpowder_damage = total_damage
if "slots" in item:
powder = powders[get_appropriate_powder_idx(item)][5] # Assume tier6 always.
postpowder_damage += powder * item["slots"]
return (dps, postpowder_damage * attack_speed_mod)
item_dat = {cat: [] for cat in weapon_type_mods}
for name, item in item_map.items():
if "lvl" not in item:
continue
if item["lvl"] >= min_level and item["category"] == "weapon":
dps, postpowder_dps = get_data(item, powders_old)
new_item = item_new_map[name]
new_dps, new_postpowder_dps = get_data(new_item, powders_new)
total, actual, explain = guess_design_modifier(item, dps)
item_dat[item["type"]].append((get_display_name(item), item["lvl"], item["id"], item["tier"],
dps, postpowder_dps,
new_dps, new_postpowder_dps,
total, actual, explain))
item_dat["baseline_xs"] = baselines_known_x.tolist()
item_dat["baseline_ys"] = baselines_known_y.tolist()
item_dat["baseline_ys_new"] = baselines_known_y_new.tolist()
json.dump(item_dat, open("dps_data.json", "w"), indent=2)
json.dump(item_dat, open("dps_data_compress.json", "w"))
# item_lvl, item_dps, item_tiercolor = zip(*item_dat)
# plt.scatter(item_lvl, item_dps, color=item_tiercolor)
#
# for tier, mod in tiers_mod.items():
# plt.plot(baselines_known_x, baselines_known_y * max_mult * mod, '--', color=tiers_colors[tier])
# plt.plot(baselines_known_x, baselines_known_y * min_mult * mod, '--', color=tiers_colors[tier])
# plt.xticks([70, 75, 80, 85, 90, 95, 100])
# plt.xlabel("Combat level")
# plt.ylabel("Base dps")
# plt.title(f"{item_type} base dps vs level")
# plt.grid()
# plt.show()