From 5afb691b309292bd01a498b2e4a7f3c3143142b4 Mon Sep 17 00:00:00 2001 From: thepaperpilot Date: Tue, 21 Mar 2023 00:15:28 -0500 Subject: [PATCH] Fix more tests --- src/game/formulas.ts | 4 +- src/game/requirements.tsx | 5 +- tests/game/formulas.test.ts | 145 +++++++++++--------------------- tests/game/requirements.test.ts | 43 +++++----- 4 files changed, 78 insertions(+), 119 deletions(-) diff --git a/src/game/formulas.ts b/src/game/formulas.ts index e812de3..3c0b379 100644 --- a/src/game/formulas.ts +++ b/src/game/formulas.ts @@ -477,9 +477,7 @@ function invertTetrate( payload: FormulaSource ) { if (hasVariable(base)) { - return base.invert( - Decimal.slog(value, Decimal.minabs(1e308, unrefFormulaSource(height)).toNumber()) - ); + return base.invert(Decimal.ssqrt(value)); } // Other params can't be inverted ATM throw "Could not invert due to no input being a variable"; diff --git a/src/game/requirements.tsx b/src/game/requirements.tsx index 9412e22..5372e36 100644 --- a/src/game/requirements.tsx +++ b/src/game/requirements.tsx @@ -82,7 +82,7 @@ export interface CostRequirementOptions { * When calculating multiple levels to be handled at once, whether it should consider resources used for each level as spent. Setting this to false causes calculations to be faster with larger numbers and supports more math functions. * @see {Formula} */ - spendResources?: Computable; + spendResources: Computable; /** * Pass-through to {@link Requirement.pay}. May be required for maximizing support. * @see {@link cost} for restrictions on maximizing support. @@ -148,8 +148,9 @@ export function createCostRequirement( setDefault(req, "visibility", Visibility.Visible); processComputable(req as T, "cost"); processComputable(req as T, "requiresPay"); - processComputable(req as T, "spendResources"); setDefault(req, "requiresPay", true); + processComputable(req as T, "spendResources"); + setDefault(req, "spendResources", false); setDefault(req, "pay", function (amount?: DecimalSource) { const cost = req.cost instanceof Formula diff --git a/tests/game/formulas.test.ts b/tests/game/formulas.test.ts index ddc0bee..43d876f 100644 --- a/tests/game/formulas.test.ts +++ b/tests/game/formulas.test.ts @@ -40,7 +40,10 @@ const invertibleZeroParamFunctionNames = [ "tanh", "asinh", "acosh", - "atanh" + "atanh", + "slog", + "tetrate", + "iteratedexp" ] as const; const nonInvertibleZeroParamFunctionNames = [ "abs", @@ -122,7 +125,7 @@ const invertibleOneParamFunctionNames = [ "log", "pow", "root", - "slog" + "layeradd" ] as const; const nonInvertibleOneParamFunctionNames = ["layeradd10"] as const; const integrableOneParamFunctionNames = ["add", "sub", "mul", "div", "log", "pow", "root"] as const; @@ -130,12 +133,8 @@ const nonIntegrableOneParamFunctionNames = [...nonInvertibleOneParamFunctionName const invertibleIntegralOneParamFunctionNames = integrableOneParamFunctionNames; const nonInvertibleIntegralOneParamFunctionNames = nonIntegrableOneParamFunctionNames; -const invertibleTwoParamFunctionNames = ["tetrate", "layeradd", "iteratedexp"] as const; const nonInvertibleTwoParamFunctionNames = ["iteratedlog", "pentate"] as const; -const nonIntegrableTwoParamFunctionNames = [ - ...invertibleTwoParamFunctionNames, - ...nonInvertibleZeroParamFunctionNames -]; +const nonIntegrableTwoParamFunctionNames = nonInvertibleTwoParamFunctionNames; const nonInvertibleIntegralTwoParamFunctionNames = nonIntegrableTwoParamFunctionNames; describe("Formula Equality Checking", () => { @@ -201,16 +200,6 @@ describe("Formula Equality Checking", () => { }); } ); - - [...invertibleTwoParamFunctionNames, ...nonInvertibleTwoParamFunctionNames].forEach( - name => { - test(name, () => { - const instanceFormula = formula[name](1, 1); - const staticFormula = Formula[name](formula, 1, 1); - expect(instanceFormula.equals(staticFormula)).toBe(true); - }); - } - ); }); }); @@ -331,13 +320,7 @@ describe("Creating Formulas", () => { ); }); describe("2-param", () => { - ( - [ - ...invertibleTwoParamFunctionNames, - ...nonInvertibleTwoParamFunctionNames, - "clamp" - ] as const - ).forEach(names => + ([...nonInvertibleTwoParamFunctionNames, "clamp"] as const).forEach(names => describe(names, () => { checkFormula(names, [0, 0, 0] as const); testValues.forEach(i => @@ -403,24 +386,6 @@ describe("Inverting", () => { checkFormula(Formula[name](variable, variable), false)); }); }); - invertibleTwoParamFunctionNames.forEach(name => { - describe(name, () => { - test(`${name}(var, const, const) is marked as invertible and having a variable`, () => - checkFormula(Formula[name](variable, constant, constant))); - test(`${name}(const, var, const) is marked as invertible and having a variable`, () => - checkFormula(Formula[name](constant, variable, constant))); - test(`${name}(const, const, var) is marked as invertible and having a variable`, () => - checkFormula(Formula[name](constant, constant, variable))); - test(`${name}(var, var, const) is marked as not invertible and not having a variable`, () => - checkFormula(Formula[name](variable, variable, constant), false)); - test(`${name}(var, const, var) is marked as not invertible and not having a variable`, () => - checkFormula(Formula[name](variable, constant, variable), false)); - test(`${name}(const, var, var) is marked as not invertible and not having a variable`, () => - checkFormula(Formula[name](constant, variable, variable), false)); - test(`${name}(var, var, var) is marked as not invertible and not having a variable`, () => - checkFormula(Formula[name](variable, variable, variable), false)); - }); - }); }); describe("Non-invertible formulas marked as such", () => { @@ -479,30 +444,13 @@ describe("Inverting", () => { const result = formula.evaluate(); expect(formula.invert(result)).compare_tolerance(2); }); - test(`${name}(const, var).invert()`, () => { - const formula = Formula[name](constant, variable); - const result = formula.evaluate(); - expect(formula.invert(result)).compare_tolerance(2); - }); - }) - ); - invertibleTwoParamFunctionNames.forEach(name => - describe(name, () => { - test(`${name}(var, const, const).invert()`, () => { - const formula = Formula[name](variable, constant, constant); - const result = formula.evaluate(); - expect(formula.invert(result)).compare_tolerance(2); - }); - test(`${name}(const, var, const).invert()`, () => { - const formula = Formula[name](constant, variable, constant); - const result = formula.evaluate(); - expect(formula.invert(result)).compare_tolerance(2); - }); - test(`${name}(const, const, var).invert()`, () => { - const formula = Formula[name](constant, constant, variable); - const result = formula.evaluate(); - expect(formula.invert(result)).compare_tolerance(2); - }); + if (name !== "layeradd") { + test(`${name}(const, var).invert()`, () => { + const formula = Formula[name](constant, variable); + const result = formula.evaluate(); + expect(formula.invert(result)).compare_tolerance(2); + }); + } }) ); }); @@ -530,7 +478,7 @@ describe("Inverting", () => { test("Inverting with non-invertible sections", () => { const formula = Formula.add(variable, constant.ceil()); expect(formula.isInvertible()).toBe(true); - expect(formula.invert(10)).compare_tolerance(7); + expect(formula.invert(10)).compare_tolerance(0); }); }); @@ -562,8 +510,10 @@ describe("Integrating", () => { describe(name, () => { test(`${name}(var, const) is marked as integrable`, () => checkFormula(Formula[name](variable, constant))); - test(`${name}(const, var) is marked as integrable`, () => - checkFormula(Formula[name](constant, variable))); + if (name !== "log" && name !== "root") { + test(`${name}(const, var) is marked as integrable`, () => + checkFormula(Formula[name](constant, variable))); + } test(`${name}(var, var) is marked as not integrable`, () => expect(Formula[name](variable, variable).isIntegrable()).toBe(false)); }); @@ -645,8 +595,10 @@ describe("Inverting integrals", () => { describe(name, () => { test(`${name}(var, const) is marked as having an invertible integral`, () => checkFormula(Formula[name](variable, constant))); - test(`${name}(const, var) is marked as having an invertible integral`, () => - checkFormula(Formula[name](constant, variable))); + if (name !== "log" && name !== "root") { + test(`${name}(const, var) is marked as having an invertible integral`, () => + checkFormula(Formula[name](constant, variable))); + } test(`${name}(var, var) is marked as not having an invertible integral`, () => { const formula = Formula[name](variable, variable); expect(formula.isIntegralInvertible()).toBe(false); @@ -871,6 +823,11 @@ describe("Conditionals", () => { }); describe("Custom Formulas", () => { + let variable: GenericFormula; + beforeAll(() => { + variable = Formula.variable(1); + }); + describe("Formula with evaluate", () => { test("Zero input evaluates correctly", () => expect(new Formula({ inputs: [], evaluate: () => 10 }).evaluate()).compare_tolerance( @@ -887,28 +844,28 @@ describe("Custom Formulas", () => { }); describe("Formula with invert", () => { - test("Zero input inverts correctly", () => - expect( + test("Zero input does not invert", () => + expect(() => new Formula({ inputs: [], evaluate: () => 6, invert: value => value, hasVariable: true }).invert(10) - ).compare_tolerance(10)); + ).toThrow()); test("One input inverts correctly", () => expect( new Formula({ - inputs: [1], + inputs: [variable], evaluate: () => 10, - invert: (value, v1) => v1, + invert: (value, v1) => v1.evaluate(), hasVariable: true }).invert(10) ).compare_tolerance(1)); test("Two inputs inverts correctly", () => expect( new Formula({ - inputs: [1, 2], + inputs: [variable, 2], evaluate: () => 10, invert: (value, v1, v2) => v2, hasVariable: true @@ -918,7 +875,7 @@ describe("Custom Formulas", () => { describe("Formula with integrate", () => { test("Zero input integrates correctly", () => - expect( + expect(() => new Formula({ inputs: [], evaluate: () => 10, @@ -928,7 +885,7 @@ describe("Custom Formulas", () => { test("One input integrates correctly", () => expect( new Formula({ - inputs: [1], + inputs: [variable], evaluate: () => 10, integrate: (val, v1) => val ?? 20 }).evaluateIntegral() @@ -936,7 +893,7 @@ describe("Custom Formulas", () => { test("Two inputs integrates correctly", () => expect( new Formula({ - inputs: [1, 2], + inputs: [variable, 2], evaluate: (v1, v2) => 10, integrate: (v1, v2) => 3 }).evaluateIntegral() @@ -944,19 +901,19 @@ describe("Custom Formulas", () => { }); describe("Formula with invertIntegral", () => { - test("Zero input inverts integral correctly", () => - expect( + test("Zero input does not invert integral", () => + expect(() => new Formula({ inputs: [], evaluate: () => 10, invertIntegral: () => 1, hasVariable: true }).invertIntegral(8) - ).compare_tolerance(1)); + ).toThrow()); test("One input inverts integral correctly", () => expect( new Formula({ - inputs: [1], + inputs: [variable], evaluate: () => 10, invertIntegral: (val, v1) => 1, hasVariable: true @@ -965,7 +922,7 @@ describe("Custom Formulas", () => { test("Two inputs inverts integral correctly", () => expect( new Formula({ - inputs: [1, 2], + inputs: [variable, 2], evaluate: (v1, v2) => 10, invertIntegral: (v1, v2) => 1, hasVariable: true @@ -977,36 +934,36 @@ describe("Custom Formulas", () => { describe("Buy Max", () => { let resource: Resource; beforeAll(() => { - resource = createResource(ref(10)); + resource = createResource(ref(1000)); }); - describe("With spending", () => { + describe("Without spending", () => { test("Throws on formula with non-invertible integral", () => { const maxAffordable = calculateMaxAffordable(Formula.neg(10), resource, false); expect(() => maxAffordable.value).toThrow(); }); // https://www.desmos.com/calculator/5vgletdc1p test("Calculates max affordable and cost correctly", () => { - const variable = Formula.variable(10); - const formula = Formula.pow(1.05, variable); + const variable = Formula.variable(0); + const formula = Formula.pow(1.05, variable).times(100); const maxAffordable = calculateMaxAffordable(formula, resource, false); expect(maxAffordable.value).compare_tolerance(47); expect(calculateCost(formula, maxAffordable.value, false)).compare_tolerance( - Decimal.pow(1.05, 47) + Decimal.pow(1.05, 47).times(100) ); }); }); - describe("Without spending", () => { + describe("With spending", () => { test("Throws on non-invertible formula", () => { const maxAffordable = calculateMaxAffordable(Formula.abs(10), resource); expect(() => maxAffordable.value).toThrow(); }); // https://www.desmos.com/calculator/5vgletdc1p test("Calculates max affordable and cost correctly", () => { - const variable = Formula.variable(10); - const formula = Formula.pow(1.05, variable); + const variable = Formula.variable(0); + const formula = Formula.pow(1.05, variable).times(100); const maxAffordable = calculateMaxAffordable(formula, resource); expect(maxAffordable.value).compare_tolerance(7); - expect(calculateCost(formula, maxAffordable.value)).compare_tolerance(7.35); + expect(calculateCost(formula, maxAffordable.value)).compare_tolerance(735); }); }); }); diff --git a/tests/game/requirements.test.ts b/tests/game/requirements.test.ts index 57e574b..55474b9 100644 --- a/tests/game/requirements.test.ts +++ b/tests/game/requirements.test.ts @@ -2,6 +2,7 @@ import { Visibility } from "features/feature"; import { createResource, Resource } from "features/resources/resource"; import Formula from "game/formulas"; import { + CostRequirement, createBooleanRequirement, createCostRequirement, createVisibilityRequirement, @@ -17,19 +18,18 @@ import "../utils"; describe("Creating cost requirement", () => { describe("Minimal requirement", () => { let resource: Resource; - let requirement: Requirement; + let requirement: CostRequirement; beforeAll(() => { resource = createResource(ref(10)); requirement = createCostRequirement(() => ({ resource, - cost: 10 + cost: 10, + spendResources: false })); }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("resource pass-through", () => expect((requirement as any).resource).toBe(resource)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("cost pass-through", () => expect((requirement as any).cost).toBe(10)); + test("resource pass-through", () => expect(requirement.resource).toBe(resource)); + test("cost pass-through", () => expect(requirement.cost).toBe(10)); test("partialDisplay exists", () => expect(typeof requirement.partialDisplay).toBe("function")); @@ -41,23 +41,22 @@ describe("Creating cost requirement", () => { }); test("is visible", () => expect(requirement.visibility).toBe(Visibility.Visible)); test("requires pay", () => expect(requirement.requiresPay).toBe(true)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("spends resources", () => expect((requirement as any).spendResources).toBe(true)); + test("does not spend resources", () => expect(requirement.spendResources).toBe(false)); test("cannot maximize", () => expect(unref(requirement.canMaximize)).toBe(false)); }); describe("Fully customized", () => { let resource: Resource; - let requirement: Requirement; + let requirement: CostRequirement; beforeAll(() => { resource = createResource(ref(10)); requirement = createCostRequirement(() => ({ resource, - cost: 10, + cost: Formula.variable(resource).times(10), visibility: Visibility.None, requiresPay: false, maximize: true, - spendResources: false, + spendResources: true, // eslint-disable-next-line @typescript-eslint/no-empty-function pay() {} })); @@ -69,9 +68,7 @@ describe("Creating cost requirement", () => { requirement.pay.length === 1); test("is not visible", () => expect(requirement.visibility).toBe(Visibility.None)); test("does not require pay", () => expect(requirement.requiresPay).toBe(false)); - test("does not spend resources", () => - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect((requirement as any).spendResources).toBe(false)); + test("spends resources", () => expect(requirement.spendResources).toBe(true)); test("can maximize", () => expect(unref(requirement.canMaximize)).toBe(true)); }); @@ -79,7 +76,8 @@ describe("Creating cost requirement", () => { const resource = createResource(ref(10)); const requirement = createCostRequirement(() => ({ resource, - cost: 10 + cost: 10, + spendResources: false })); expect(unref(requirement.requirementMet)).toBe(true); }); @@ -88,7 +86,8 @@ describe("Creating cost requirement", () => { const resource = createResource(ref(10)); const requirement = createCostRequirement(() => ({ resource, - cost: 100 + cost: 100, + spendResources: false })); expect(unref(requirement.requirementMet)).toBe(false); }); @@ -148,7 +147,8 @@ describe("Checking maximum levels of requirements met", () => { createBooleanRequirement(true), createCostRequirement(() => ({ resource: createResource(ref(10)), - cost: Formula.variable(0) + cost: Formula.variable(0), + spendResources: false })) ]; expect(maxRequirementsMet(requirements)).compare_tolerance(0); @@ -159,7 +159,8 @@ describe("Checking maximum levels of requirements met", () => { createBooleanRequirement(true), createCostRequirement(() => ({ resource: createResource(ref(10)), - cost: Formula.variable(0) + cost: Formula.variable(0), + spendResources: false })) ]; expect(maxRequirementsMet(requirements)).compare_tolerance(10); @@ -171,11 +172,13 @@ test("Paying requirements", () => { const noPayment = createCostRequirement(() => ({ resource, cost: 10, - requiresPay: false + requiresPay: false, + spendResources: false })); const payment = createCostRequirement(() => ({ resource, - cost: 10 + cost: 10, + spendResources: false })); payRequirements([noPayment, payment]); expect(resource.value).compare_tolerance(90);