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 internalIntegrate: IntegrateFunction<T> | undefined;
|
||||||
protected readonly internalIntegrateInner: IntegrateFunction<T> | undefined;
|
protected readonly internalIntegrateInner: IntegrateFunction<T> | undefined;
|
||||||
protected readonly applySubstitution: SubstitutionFunction<T> | undefined;
|
protected readonly applySubstitution: SubstitutionFunction<T> | undefined;
|
||||||
|
protected readonly description: string | undefined;
|
||||||
protected readonly internalVariables: number;
|
protected readonly internalVariables: number;
|
||||||
|
|
||||||
public readonly innermostVariable: ProcessedComputable<DecimalSource> | undefined;
|
public readonly innermostVariable: ProcessedComputable<DecimalSource> | undefined;
|
||||||
|
@ -85,6 +86,7 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
||||||
this.internalIntegrate = readonlyProperties.internalIntegrate;
|
this.internalIntegrate = readonlyProperties.internalIntegrate;
|
||||||
this.internalIntegrateInner = readonlyProperties.internalIntegrateInner;
|
this.internalIntegrateInner = readonlyProperties.internalIntegrateInner;
|
||||||
this.applySubstitution = readonlyProperties.applySubstitution;
|
this.applySubstitution = readonlyProperties.applySubstitution;
|
||||||
|
this.description = options.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupVariable({
|
private setupVariable({
|
||||||
|
@ -216,6 +218,25 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
||||||
return new Formula({ variable: value });
|
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
|
// TODO add integration support to step-wise functions
|
||||||
/**
|
/**
|
||||||
* Creates a step-wise formula. After {@link start} the formula will have an additional modifier.
|
* 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({
|
return new Formula({
|
||||||
inputs: [value],
|
inputs: [value],
|
||||||
evaluate: evalStep,
|
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({
|
return new Formula({
|
||||||
inputs: [value],
|
inputs: [value],
|
||||||
evaluate: evalStep,
|
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(
|
public static conditional(
|
||||||
|
@ -878,6 +903,10 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public stringify() {
|
||||||
|
return Formula.stringify(this);
|
||||||
|
}
|
||||||
|
|
||||||
public step(
|
public step(
|
||||||
start: Computable<DecimalSource>,
|
start: Computable<DecimalSource>,
|
||||||
formulaModifier: (value: InvertibleIntegralFormula) => GenericFormula
|
formulaModifier: (value: InvertibleIntegralFormula) => GenericFormula
|
||||||
|
@ -1402,28 +1431,6 @@ export function findNonInvertible(formula: GenericFormula): GenericFormula | nul
|
||||||
return null;
|
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.
|
* 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
|
* @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
|
...inputs: T
|
||||||
) => GenericFormula;
|
) => GenericFormula;
|
||||||
|
|
||||||
type VariableFormulaOptions = { variable: ProcessedComputable<DecimalSource> };
|
type VariableFormulaOptions = {
|
||||||
|
variable: ProcessedComputable<DecimalSource>;
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
type ConstantFormulaOptions = {
|
type ConstantFormulaOptions = {
|
||||||
inputs: [FormulaSource];
|
inputs: [FormulaSource];
|
||||||
|
description?: string;
|
||||||
};
|
};
|
||||||
type GeneralFormulaOptions<T extends [FormulaSource] | FormulaSource[]> = {
|
type GeneralFormulaOptions<T extends [FormulaSource] | FormulaSource[]> = {
|
||||||
inputs: T;
|
inputs: T;
|
||||||
|
@ -48,6 +52,7 @@ type GeneralFormulaOptions<T extends [FormulaSource] | FormulaSource[]> = {
|
||||||
integrate?: IntegrateFunction<T>;
|
integrate?: IntegrateFunction<T>;
|
||||||
integrateInner?: IntegrateFunction<T>;
|
integrateInner?: IntegrateFunction<T>;
|
||||||
applySubstitution?: SubstitutionFunction<T>;
|
applySubstitution?: SubstitutionFunction<T>;
|
||||||
|
description?: string;
|
||||||
};
|
};
|
||||||
type FormulaOptions<T extends [FormulaSource] | FormulaSource[]> =
|
type FormulaOptions<T extends [FormulaSource] | FormulaSource[]> =
|
||||||
| VariableFormulaOptions
|
| VariableFormulaOptions
|
||||||
|
@ -63,6 +68,7 @@ type InternalFormulaProperties<T extends [FormulaSource] | FormulaSource[]> = {
|
||||||
internalIntegrateInner?: IntegrateFunction<T>;
|
internalIntegrateInner?: IntegrateFunction<T>;
|
||||||
applySubstitution?: SubstitutionFunction<T>;
|
applySubstitution?: SubstitutionFunction<T>;
|
||||||
innermostVariable?: ProcessedComputable<DecimalSource>;
|
innermostVariable?: ProcessedComputable<DecimalSource>;
|
||||||
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SubstitutionStack = ((value: GenericFormula) => GenericFormula)[] | undefined;
|
type SubstitutionStack = ((value: GenericFormula) => GenericFormula)[] | undefined;
|
||||||
|
|
|
@ -155,7 +155,7 @@ describe("Formula Equality Checking", () => {
|
||||||
describe("Formula aliases", () => {
|
describe("Formula aliases", () => {
|
||||||
function testAliases<T extends FormulaFunctions>(
|
function testAliases<T extends FormulaFunctions>(
|
||||||
aliases: T[],
|
aliases: T[],
|
||||||
args: Parameters<typeof Formula[T]>
|
args: Parameters<(typeof Formula)[T]>
|
||||||
) {
|
) {
|
||||||
describe(aliases[0], () => {
|
describe(aliases[0], () => {
|
||||||
let formula: GenericFormula;
|
let formula: GenericFormula;
|
||||||
|
@ -250,7 +250,7 @@ describe("Creating Formulas", () => {
|
||||||
|
|
||||||
function checkFormula<T extends FormulaFunctions>(
|
function checkFormula<T extends FormulaFunctions>(
|
||||||
functionName: T,
|
functionName: T,
|
||||||
args: Readonly<Parameters<typeof Formula[T]>>
|
args: Readonly<Parameters<(typeof Formula)[T]>>
|
||||||
) {
|
) {
|
||||||
let formula: GenericFormula;
|
let formula: GenericFormula;
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
|
@ -274,7 +274,7 @@ describe("Creating Formulas", () => {
|
||||||
// It's a lot of tests, but I'd rather be exhaustive
|
// It's a lot of tests, but I'd rather be exhaustive
|
||||||
function testFormulaCall<T extends FormulaFunctions>(
|
function testFormulaCall<T extends FormulaFunctions>(
|
||||||
functionName: T,
|
functionName: T,
|
||||||
args: Readonly<Parameters<typeof Formula[T]>>
|
args: Readonly<Parameters<(typeof Formula)[T]>>
|
||||||
) {
|
) {
|
||||||
if ((functionName === "slog" || functionName === "layeradd") && args[0] === -1) {
|
if ((functionName === "slog" || functionName === "layeradd") && args[0] === -1) {
|
||||||
// These cases in particular take a long time, so skip them
|
// 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 { CoercableComponent, JSXFunction } from "features/feature";
|
||||||
import Formula, { printFormula } from "game/formulas/formulas";
|
import Formula from "game/formulas/formulas";
|
||||||
import {
|
import {
|
||||||
createAdditiveModifier,
|
createAdditiveModifier,
|
||||||
createExponentialModifier,
|
createExponentialModifier,
|
||||||
|
@ -52,7 +52,7 @@ function testModifiers<
|
||||||
expect(modifier.invert(operation(10, 5))).compare_tolerance(10));
|
expect(modifier.invert(operation(10, 5))).compare_tolerance(10));
|
||||||
test("getFormula returns the right formula", () => {
|
test("getFormula returns the right formula", () => {
|
||||||
const value = ref(10);
|
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)`
|
`${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));
|
expect(modifier.invert(Decimal.add(10, 5).times(5).pow(5))).compare_tolerance(10));
|
||||||
test("getFormula returns the right formula", () => {
|
test("getFormula returns the right formula", () => {
|
||||||
const value = ref(10);
|
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)`
|
`pow(mul(add(x, 5.00), 5.00), 5.00)`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue