Files
cabrits-math/lib/problems/generators/generators.test.ts
2026-07-05 11:12:45 -04:00

73 lines
2.6 KiB
TypeScript

import { describe, it, expect } from "vitest";
import type { Difficulty, MathProblem } from "../types";
import { generateFractionBodmas, generateSignedFraction } from "./fraction-problems";
import { generateDefineRatio, generateFractionsAndRatios } from "./ratio-problems";
import { generateIntegerOrderCompare, generateIntegerAddSubtractMixed } from "./integer-problems";
import { generateQuaternaryToDenary } from "./number-system-problems";
function assertWellFormed(p: MathProblem) {
expect(typeof p.prompt).toBe("string");
expect(p.prompt.length).toBeGreaterThan(0);
expect(Array.isArray(p.hints)).toBe(true);
expect(Array.isArray(p.steps)).toBe(true);
const a = p.answer;
if (a.kind === "integer") {
expect(Number.isFinite(a.value)).toBe(true);
} else if (a.kind === "fraction") {
expect(Number.isInteger(a.numerator)).toBe(true);
expect(Number.isInteger(a.denominator)).toBe(true);
expect(a.denominator).not.toBe(0);
} else if (a.kind === "ratio") {
expect(a.parts.length).toBeGreaterThan(0);
a.parts.forEach((v) => expect(Number.isFinite(v)).toBe(true));
}
}
const NEW_GENERATORS: Record<string, (d: Difficulty) => MathProblem> = {
"fraction BODMAS": generateFractionBodmas,
"signed fraction": generateSignedFraction,
"define ratio": generateDefineRatio,
"fractions and ratios": generateFractionsAndRatios,
"integer order/compare": generateIntegerOrderCompare,
"integer add/subtract (mixed)": generateIntegerAddSubtractMixed,
"quaternary to denary": generateQuaternaryToDenary,
};
describe("new practice generators produce well-formed problems", () => {
for (const [name, gen] of Object.entries(NEW_GENERATORS)) {
it(name, () => {
for (const d of [1, 2, 3] as Difficulty[]) {
for (let i = 0; i < 40; i++) assertWellFormed(gen(d));
}
});
}
});
describe("quaternary answer matches its base-4 prompt", () => {
it("converts base 4 correctly", () => {
for (const d of [1, 2, 3] as Difficulty[]) {
for (let i = 0; i < 40; i++) {
const p = generateQuaternaryToDenary(d);
const digits = p.prompt.match(/^(\d+)_\{4\}/)?.[1];
expect(digits).toBeTruthy();
if (p.answer.kind === "integer") {
expect(p.answer.value).toBe(parseInt(digits!, 4));
}
}
}
});
});
describe("order/compare answer is one of the numbers asked about", () => {
it("picks a value from the prompt", () => {
for (let i = 0; i < 60; i++) {
const p = generateIntegerOrderCompare(1);
const nums = (p.prompt.match(/-?\d+/g) ?? []).map(Number);
if (p.answer.kind === "integer") {
expect(nums).toContain(p.answer.value);
}
}
});
});