forked from profectus/Profectus
Fix crash when calculating formula cost
Happened when spend resource was false and the formula was non-integrable, but the amount to buy were all going to be summed anyways
This commit is contained in:
parent
6786c27b89
commit
4d7f03d543
2 changed files with 40 additions and 21 deletions
|
@ -1491,33 +1491,35 @@ export function calculateCost(
|
||||||
spendResources = true,
|
spendResources = true,
|
||||||
summedPurchases?: number
|
summedPurchases?: number
|
||||||
) {
|
) {
|
||||||
let newValue = Decimal.add(amountToBuy, unref(formula.innermostVariable) ?? 0);
|
const origValue = unref(formula.innermostVariable) ?? 0;
|
||||||
|
let newValue = Decimal.add(amountToBuy, origValue);
|
||||||
|
const targetValue = newValue;
|
||||||
|
summedPurchases ??= spendResources ? 10 : 0;
|
||||||
|
newValue = newValue.sub(summedPurchases).clampMin(origValue);
|
||||||
|
let cost: DecimalSource = 0;
|
||||||
if (spendResources) {
|
if (spendResources) {
|
||||||
if (!formula.isIntegrable()) {
|
if (Decimal.gt(amountToBuy, summedPurchases)) {
|
||||||
throw new Error(
|
if (!formula.isIntegrable()) {
|
||||||
"Cannot calculate cost with spending resources of non-integrable formula"
|
throw new Error(
|
||||||
);
|
"Cannot calculate cost with spending resources of non-integrable formula"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cost = Decimal.sub(formula.evaluateIntegral(newValue), formula.evaluateIntegral());
|
||||||
}
|
}
|
||||||
const targetValue = newValue;
|
|
||||||
newValue = newValue
|
|
||||||
.sub(summedPurchases ?? 10)
|
|
||||||
.clampMin(unref(formula.innermostVariable) ?? 0);
|
|
||||||
let cost = Decimal.sub(formula.evaluateIntegral(newValue), formula.evaluateIntegral());
|
|
||||||
if (targetValue.gt(1e308)) {
|
if (targetValue.gt(1e308)) {
|
||||||
// Too large of a number for summedPurchases to make a difference,
|
// Too large of a number for summedPurchases to make a difference,
|
||||||
// just get the cost and multiply by summed purchases
|
// just get the cost and multiply by summed purchases
|
||||||
return cost.add(Decimal.sub(targetValue, newValue).times(formula.evaluate(newValue)));
|
return Decimal.add(
|
||||||
|
cost,
|
||||||
|
Decimal.sub(targetValue, newValue).times(formula.evaluate(newValue))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for (let i = newValue.toNumber(); i < targetValue.toNumber(); i++) {
|
for (let i = newValue.toNumber(); i < targetValue.toNumber(); i++) {
|
||||||
cost = cost.add(formula.evaluate(i));
|
cost = Decimal.add(cost, formula.evaluate(i));
|
||||||
}
|
}
|
||||||
return cost;
|
|
||||||
} else {
|
} else {
|
||||||
const targetValue = newValue;
|
cost = formula.evaluate(newValue);
|
||||||
newValue = newValue
|
newValue = newValue.add(1);
|
||||||
.sub(summedPurchases ?? 0)
|
|
||||||
.clampMin(unref(formula.innermostVariable) ?? 0);
|
|
||||||
let cost = formula.evaluate(newValue);
|
|
||||||
if (targetValue.gt(1e308)) {
|
if (targetValue.gt(1e308)) {
|
||||||
// Too large of a number for summedPurchases to make a difference,
|
// Too large of a number for summedPurchases to make a difference,
|
||||||
// just get the cost and multiply by summed purchases
|
// just get the cost and multiply by summed purchases
|
||||||
|
@ -1526,6 +1528,6 @@ export function calculateCost(
|
||||||
for (let i = newValue.toNumber(); i < targetValue.toNumber(); i++) {
|
for (let i = newValue.toNumber(); i < targetValue.toNumber(); i++) {
|
||||||
cost = Decimal.add(cost, formula.evaluate(i));
|
cost = Decimal.add(cost, formula.evaluate(i));
|
||||||
}
|
}
|
||||||
return cost;
|
|
||||||
}
|
}
|
||||||
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1089,9 +1089,21 @@ describe("Buy Max", () => {
|
||||||
Decimal.pow(1.05, 141).times(100)
|
Decimal.pow(1.05, 141).times(100)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
test("Calculates max affordable and cost correctly with summing last purchases", () => {
|
||||||
|
const variable = Formula.variable(0);
|
||||||
|
const formula = Formula.pow(1.05, variable).times(100);
|
||||||
|
const maxAffordable = calculateMaxAffordable(formula, resource, false, 4);
|
||||||
|
expect(maxAffordable.value).compare_tolerance(141 - 4);
|
||||||
|
|
||||||
|
const actualCost = new Array(4)
|
||||||
|
.fill(null)
|
||||||
|
.reduce((acc, _, i) => acc.add(formula.evaluate(133 + i)), new Decimal(0));
|
||||||
|
const calculatedCost = calculateCost(formula, maxAffordable.value, false, 4);
|
||||||
|
expect(calculatedCost).compare_tolerance(actualCost);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe("With spending", () => {
|
describe("With spending", () => {
|
||||||
test("Throws on non-invertible formula", () => {
|
test("Throws on calculating max affordable of non-invertible formula", () => {
|
||||||
const maxAffordable = calculateMaxAffordable(Formula.abs(10), resource);
|
const maxAffordable = calculateMaxAffordable(Formula.abs(10), resource);
|
||||||
expect(() => maxAffordable.value).toThrow();
|
expect(() => maxAffordable.value).toThrow();
|
||||||
});
|
});
|
||||||
|
@ -1220,7 +1232,7 @@ describe("Buy Max", () => {
|
||||||
(acc, _, i) => acc.add(formula.evaluate(i + purchases.value)),
|
(acc, _, i) => acc.add(formula.evaluate(i + purchases.value)),
|
||||||
new Decimal(0)
|
new Decimal(0)
|
||||||
);
|
);
|
||||||
const calculatedCost = calculateCost(formula, maxAffordable.value, true);
|
const calculatedCost = calculateCost(formula, maxAffordable.value);
|
||||||
// Since we're summing all the purchases this should be equivalent
|
// Since we're summing all the purchases this should be equivalent
|
||||||
expect(calculatedCost).compare_tolerance(actualCost);
|
expect(calculatedCost).compare_tolerance(actualCost);
|
||||||
});
|
});
|
||||||
|
@ -1235,5 +1247,10 @@ describe("Buy Max", () => {
|
||||||
expect(Decimal.isFinite(calculatedCost)).toBe(true);
|
expect(Decimal.isFinite(calculatedCost)).toBe(true);
|
||||||
resource.value = 100000;
|
resource.value = 100000;
|
||||||
});
|
});
|
||||||
|
test("Handles summing purchases of non-integrable formula", () => {
|
||||||
|
const purchases = ref(0);
|
||||||
|
const formula = Formula.variable(purchases).abs();
|
||||||
|
expect(() => calculateCost(formula, 10)).not.toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue