Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 16 additions & 29 deletions ui/core/components/suggest_reforges_action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import i18n from '../../i18n/config.js';
import * as Mechanics from '../constants/mechanics.js';
import { IndividualSimUI } from '../individual_sim_ui';
import { Player } from '../player';
import { Class, GemColor, ItemSlot, Profession, PseudoStat, Race, ReforgeStat, Spec, Stat, UnitStats } from '../proto/common';
import { Class, GemColor, ItemSlot, Profession, PseudoStat, Race, ReforgeStat, Spec, Stat } from '../proto/common';
import { UIGem as Gem, IndividualSimSettings, ReforgeSettings, StatCapType } from '../proto/ui';
import { isShaTouchedWeapon, isThroneOfThunderWeapon, ReforgeData } from '../proto_utils/equipped_item';
import { Gear } from '../proto_utils/gear';
Expand All @@ -27,7 +27,7 @@ import { renderSavedEPWeights } from './saved_data_managers/ep_weights';
import Toast from './toast';
import { trackEvent, trackPageView } from '../../tracking/utils';
import { getReforgeWorkerPool } from '../reforge_worker_pool.js';
import type { LPModel, LPSolution, SerializedConstraints, SerializedVariables, SerializedConstraint } from '../../worker/reforge_types';
import type { LPModel, LPSolution, SerializedConstraints, SerializedVariables } from '../../worker/reforge_types';

type YalpsCoefficients = Map<string, number>;
type YalpsVariables = Map<string, YalpsCoefficients>;
Expand All @@ -41,14 +41,6 @@ function serializeVariables(variables: YalpsVariables): SerializedVariables {
return result;
}

function deserializeVariables(serialized: SerializedVariables): YalpsVariables {
const result = new Map<string, YalpsCoefficients>();
for (const [key, coefficients] of Object.entries(serialized)) {
result.set(key, new Map(Object.entries(coefficients)));
}
return result;
}

function serializeConstraints(constraints: YalpsConstraints): SerializedConstraints {
const result: SerializedConstraints = {};
for (const [key, constraint] of constraints.entries()) {
Expand Down Expand Up @@ -1717,6 +1709,7 @@ export class ReforgeOptimizer {
console.log(updatedVariables);
console.log(constraints);
}

const model: LPModel = {
direction: 'maximize',
objective: 'score',
Expand Down Expand Up @@ -1868,10 +1861,12 @@ export class ReforgeOptimizer {
}
}
}

if (isDevMode()) {
console.log('Total stat contribution from Reforging:');
console.log(reforgeStatContribution);
}

// Then check whether any unconstrained stats exceed their cap
let anyCapsExceeded = false;
const updatedConstraints = new Map<string, Constraint>(constraints);
Expand All @@ -1884,6 +1879,7 @@ export class ReforgeOptimizer {
if (cap !== 0 && value > cap && !constraints.has(statName)) {
anyCapsExceeded = true;
if (isDevMode()) console.log('Cap exceeded for: %s', statName);

// Set EP to 0 for hard capped stats unless they are treated as upper bounds.
if (this.undershootCaps.getUnitStat(unitStat)) {
updatedConstraints.set(statName, lessEq(cap));
Expand All @@ -1895,27 +1891,18 @@ export class ReforgeOptimizer {
}

// If hard caps are all taken care of, then deal with any remaining soft cap breakpoints
// Check ALL soft caps in each iteration, not just one at a time
let softCapIndex = 0;
while (softCapIndex < reforgeSoftCaps.length) {
const nextSoftCap = reforgeSoftCaps[softCapIndex];
while (!anyCapsExceeded && reforgeSoftCaps.length > 0) {
const nextSoftCap = reforgeSoftCaps[0];
const unitStat = nextSoftCap.unitStat;
const statName = unitStat.getKey();
const currentValue = reforgeStatContribution.getUnitStat(unitStat);

let idx = 0;
let breakpointExceeded = false;
for (const breakpoint of nextSoftCap.breakpoints) {
if (currentValue > breakpoint) {
// Only update the constraint if it's higher than any existing constraint
// This ensures soft cap breakpoints are "locked in" across iterations
const existingConstraint = updatedConstraints.get(statName);
if (!existingConstraint || breakpoint > (existingConstraint.min ?? 0)) {
updatedConstraints.set(statName, greaterEq(breakpoint));
}
updatedConstraints.set(statName, greaterEq(breakpoint));
updatedWeights = updatedWeights.withUnitStat(unitStat, nextSoftCap.postCapEPs[idx]);
anyCapsExceeded = true;
breakpointExceeded = true;
if (isDevMode()) console.log('Breakpoint exceeded for: %s', statName);
break;
}
Expand All @@ -1925,17 +1912,16 @@ export class ReforgeOptimizer {

// For true soft cap stats (evaluated in ascending order), remove any breakpoint that was
// exceeded from the configuration. If no breakpoints were exceeded or there are none
// remaining, then remove the entry completely from reforgeSoftCaps. For threshold
// stats, remove the entry only if a breakpoint was exceeded (locked in).
if (nextSoftCap.capType == StatCapType.TypeSoftCap && breakpointExceeded) {
// remaining, then remove the entry completely from reforgeSoftCaps. In contrast, for threshold
// stats (evaluated in descending order), always remove the entry completely after the first
// pass.
if (nextSoftCap.capType == StatCapType.TypeSoftCap) {
nextSoftCap.breakpoints = nextSoftCap.breakpoints.slice(idx + 1);
nextSoftCap.postCapEPs = nextSoftCap.postCapEPs.slice(idx + 1);
}

if ((nextSoftCap.capType == StatCapType.TypeThreshold && breakpointExceeded) || nextSoftCap.breakpoints.length == 0) {
reforgeSoftCaps.splice(softCapIndex, 1);
} else {
softCapIndex++;
if (nextSoftCap.capType == StatCapType.TypeThreshold || nextSoftCap.breakpoints.length == 0) {
reforgeSoftCaps.shift();
}
}

Expand Down Expand Up @@ -2107,6 +2093,7 @@ export class ReforgeOptimizer {

onReforgeError(error: any) {
if (isDevMode()) console.log(error);

if (this.previousGear) this.updateGear(this.previousGear);
trackEvent({
action: 'settings',
Expand Down