Move printFormula to Formula.stringify and add tests for it #59
4 changed files with 57 additions and 31 deletions
|
@ -56,6 +56,7 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
protected readonly internalIntegrate: IntegrateFunction<T> | undefined;
|
||||
protected readonly internalIntegrateInner: IntegrateFunction<T> | undefined;
|
||||
protected readonly applySubstitution: SubstitutionFunction<T> | undefined;
|
||||
protected readonly description: string | undefined;
|
||||
protected readonly internalVariables: number;
|
||||
|
||||
public readonly innermostVariable: ProcessedComputable<DecimalSource> | undefined;
|
||||
|
@ -85,6 +86,7 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
this.internalIntegrate = readonlyProperties.internalIntegrate;
|
||||
this.internalIntegrateInner = readonlyProperties.internalIntegrateInner;
|
||||
this.applySubstitution = readonlyProperties.applySubstitution;
|
||||
this.description = options.description;
|
||||
}
|
||||
|
||||
private setupVariable({
|
||||
|
@ -216,6 +218,25 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
return new Formula({ variable: value });
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies the formula so it's more easy to read in the console
|
||||
* @param formula The formula source to print, used for mapping inputs
|
||||
*/
|
||||
public static stringify(formula: FormulaSource): string {
|
||||
if (formula instanceof InternalFormula) {
|
||||
if (formula.description != null) {
|
||||
return formula.description;
|
||||
}
|
||||
if (formula.internalEvaluate == null) {
|
||||
return formula.hasVariable() ? "x" : format(formula.inputs[0] ?? 0);
|
||||
}
|
||||
return `${formula.internalEvaluate.name}(${formula.inputs
|
||||
.map(Formula.stringify)
|
||||
.join(", ")})`;
|
||||
}
|
||||
return format(unref(formula));
|
||||
}
|
||||
|
||||
// TODO add integration support to step-wise functions
|
||||
/**
|
||||
* Creates a step-wise formula. After {@link start} the formula will have an additional modifier.
|
||||
|
@ -256,7 +277,9 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
return new Formula({
|
||||
inputs: [value],
|
||||
evaluate: evalStep,
|
||||
invert: formula.isInvertible() && formula.hasVariable() ? invertStep : undefined
|
||||
invert: formula.isInvertible() && formula.hasVariable() ? invertStep : undefined,
|
||||
// Can't do anything more descriptive, due to formula's input always being a variable
|
||||
description: "indeterminate"
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -309,7 +332,9 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
return new Formula({
|
||||
inputs: [value],
|
||||
evaluate: evalStep,
|
||||
invert: formula.isInvertible() && formula.hasVariable() ? invertStep : undefined
|
||||
invert: formula.isInvertible() && formula.hasVariable() ? invertStep : undefined,
|
||||
// Can't do anything more descriptive, due to formula's input always being a variable
|
||||
description: "indeterminate"
|
||||
});
|
||||
}
|
||||
public static conditional(
|
||||
|
@ -878,6 +903,10 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
});
|
||||
}
|
||||
|
||||
public stringify() {
|
||||
return Formula.stringify(this);
|
||||
}
|
||||
|
||||
public step(
|
||||
start: Computable<DecimalSource>,
|
||||
formulaModifier: (value: InvertibleIntegralFormula) => GenericFormula
|
||||
|
@ -1402,28 +1431,6 @@ export function findNonInvertible(formula: GenericFormula): GenericFormula | nul
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies a formula so it's more easy to read in the console
|
||||
* @param formula The formula to print
|
||||
*/
|
||||
export function printFormula(formula: FormulaSource): string {
|
||||
if (formula instanceof InternalFormula) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return formula.internalEvaluate == null
|
||||
? formula.hasVariable()
|
||||
? "x"
|
||||
: formula.inputs[0] ?? 0
|
||||
: // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
formula.internalEvaluate.name +
|
||||
"(" +
|
||||
formula.inputs.map(printFormula).join(", ") +
|
||||
")";
|
||||
}
|
||||
return format(unref(formula));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for calculating the maximum amount of purchases possible with a given formula and resource. If {@link cumulativeCost} is changed to false, the calculation will be much faster with higher numbers.
|
||||
* @param formula The formula to use for calculating buy max from
|
||||
|
|
8
src/game/formulas/types.d.ts
vendored
8
src/game/formulas/types.d.ts
vendored
|
@ -37,9 +37,13 @@ type SubstitutionFunction<T> = (
|
|||
...inputs: T
|
||||
) => GenericFormula;
|
||||
|
||||
type VariableFormulaOptions = { variable: ProcessedComputable<DecimalSource> };
|
||||
type VariableFormulaOptions = {
|
||||
variable: ProcessedComputable<DecimalSource>;
|
||||
description?: string;
|
||||
};
|
||||
type ConstantFormulaOptions = {
|
||||
inputs: [FormulaSource];
|
||||
description?: string;
|
||||
};
|
||||
type GeneralFormulaOptions<T extends [FormulaSource] | FormulaSource[]> = {
|
||||
inputs: T;
|
||||
|
@ -48,6 +52,7 @@ type GeneralFormulaOptions<T extends [FormulaSource] | FormulaSource[]> = {
|
|||
integrate?: IntegrateFunction<T>;
|
||||
integrateInner?: IntegrateFunction<T>;
|
||||
applySubstitution?: SubstitutionFunction<T>;
|
||||
description?: string;
|
||||
};
|
||||
type FormulaOptions<T extends [FormulaSource] | FormulaSource[]> =
|
||||
| VariableFormulaOptions
|
||||
|
@ -63,6 +68,7 @@ type InternalFormulaProperties<T extends [FormulaSource] | FormulaSource[]> = {
|
|||
internalIntegrateInner?: IntegrateFunction<T>;
|
||||
applySubstitution?: SubstitutionFunction<T>;
|
||||
innermostVariable?: ProcessedComputable<DecimalSource>;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type SubstitutionStack = ((value: GenericFormula) => GenericFormula)[] | undefined;
|
||||
|
|
|
@ -155,7 +155,7 @@ describe("Formula Equality Checking", () => {
|
|||
describe("Formula aliases", () => {
|
||||
function testAliases<T extends FormulaFunctions>(
|
||||
aliases: T[],
|
||||
args: Parameters<typeof Formula[T]>
|
||||
args: Parameters<(typeof Formula)[T]>
|
||||
) {
|
||||
describe(aliases[0], () => {
|
||||
let formula: GenericFormula;
|
||||
|
@ -250,7 +250,7 @@ describe("Creating Formulas", () => {
|
|||
|
||||
function checkFormula<T extends FormulaFunctions>(
|
||||
functionName: T,
|
||||
args: Readonly<Parameters<typeof Formula[T]>>
|
||||
args: Readonly<Parameters<(typeof Formula)[T]>>
|
||||
) {
|
||||
let formula: GenericFormula;
|
||||
beforeAll(() => {
|
||||
|
@ -274,7 +274,7 @@ describe("Creating Formulas", () => {
|
|||
// It's a lot of tests, but I'd rather be exhaustive
|
||||
function testFormulaCall<T extends FormulaFunctions>(
|
||||
functionName: T,
|
||||
args: Readonly<Parameters<typeof Formula[T]>>
|
||||
args: Readonly<Parameters<(typeof Formula)[T]>>
|
||||
) {
|
||||
if ((functionName === "slog" || functionName === "layeradd") && args[0] === -1) {
|
||||
// These cases in particular take a long time, so skip them
|
||||
|
@ -1275,3 +1275,16 @@ describe("Buy Max", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Stringifies", () => {
|
||||
test("Nested formula", () => {
|
||||
const variable = Formula.variable(ref(0));
|
||||
expect(variable.add(5).pow(Formula.constant(10)).stringify()).toBe(
|
||||
"pow(add(x, 5.00), 10.00)"
|
||||
);
|
||||
});
|
||||
test("Indeterminate", () => {
|
||||
expect(Formula.if(10, true, f => f.add(5)).stringify()).toBe("indeterminate");
|
||||
expect(Formula.step(10, 5, f => f.add(5)).stringify()).toBe("indeterminate");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { CoercableComponent, JSXFunction } from "features/feature";
|
||||
import Formula, { printFormula } from "game/formulas/formulas";
|
||||
import Formula from "game/formulas/formulas";
|
||||
import {
|
||||
createAdditiveModifier,
|
||||
createExponentialModifier,
|
||||
|
@ -52,7 +52,7 @@ function testModifiers<
|
|||
expect(modifier.invert(operation(10, 5))).compare_tolerance(10));
|
||||
test("getFormula returns the right formula", () => {
|
||||
const value = ref(10);
|
||||
expect(printFormula(modifier.getFormula(Formula.variable(value)))).toBe(
|
||||
expect(modifier.getFormula(Formula.variable(value)).stringify()).toBe(
|
||||
`${operation.name}(x, 5.00)`
|
||||
);
|
||||
});
|
||||
|
@ -156,7 +156,7 @@ describe("Sequential Modifiers", () => {
|
|||
expect(modifier.invert(Decimal.add(10, 5).times(5).pow(5))).compare_tolerance(10));
|
||||
test("getFormula returns the right formula", () => {
|
||||
const value = ref(10);
|
||||
expect(printFormula(modifier.getFormula(Formula.variable(value)))).toBe(
|
||||
expect(modifier.getFormula(Formula.variable(value)).stringify()).toBe(
|
||||
`pow(mul(add(x, 5.00), 5.00), 5.00)`
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue