Files
cabrits-math/lib/problems/generators/ratio-problems.ts
2026-04-19 20:32:59 -04:00

111 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { MathProblem, Difficulty } from "../types";
import { randomInt, randomChoice } from "@/lib/utils";
import { simplifyRatio, divideInRatio } from "@/lib/math/ratios";
let counter = 0;
function nextId() {
return `rp-${++counter}-${Date.now()}`;
}
export function generateSimplifyRatio(difficulty: Difficulty): MathProblem {
const gcd = randomChoice(difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [3, 4, 5, 6, 7] : [4, 6, 8, 9, 12]);
const a = randomInt(1, difficulty === 1 ? 5 : 10) * gcd;
const b = randomInt(1, difficulty === 1 ? 5 : 10) * gcd;
const simplified = simplifyRatio([a, b]);
return {
id: nextId(),
prompt: `\\text{Simplify the ratio } ${a}:${b}`,
answer: { kind: "ratio", parts: simplified },
hints: [
`Find the GCD of ${a} and ${b}`,
`The GCD is ${gcd}`,
`Divide both parts by ${gcd}`,
],
steps: [
{ explanation: `GCD of ${a} and ${b} is ${gcd}` },
{ explanation: `${a} ÷ ${gcd} = ${simplified[0]}, ${b} ÷ ${gcd} = ${simplified[1]}` },
{ explanation: `Simplified: ${simplified.join(":")}` },
],
};
}
export function generateDivideInRatio(difficulty: Difficulty): MathProblem {
const numParts = difficulty === 3 ? 3 : 2;
const parts: number[] = [];
for (let i = 0; i < numParts; i++) {
parts.push(randomInt(1, difficulty === 1 ? 5 : 9));
}
const sum = parts.reduce((a, b) => a + b, 0);
const multiplier = randomChoice(difficulty === 1 ? [2, 3, 4, 5] : [3, 4, 5, 6, 7, 8]);
const total = sum * multiplier;
const values = divideInRatio(total, parts);
const intValues = values.map(Math.round);
const names = ["first share", "second share", "third share"];
return {
id: nextId(),
prompt: `\\text{Share } ${total} \\text{ in the ratio } ${parts.join(":")}`,
answer: { kind: "ratio", parts: intValues },
hints: [
`Total parts: ${parts.join(" + ")} = ${sum}`,
`One part = ${total} ÷ ${sum} = ${multiplier}`,
`Multiply each ratio number by ${multiplier}`,
],
steps: [
{ explanation: `Total parts = ${parts.join(" + ")} = ${sum}` },
{ explanation: `One part = ${total} ÷ ${sum} = ${multiplier}` },
...parts.map((p, i) => ({
explanation: `${names[i]}: ${p} × ${multiplier} = ${intValues[i]}`,
})),
],
};
}
export function generateRatioWordProblem(difficulty: Difficulty): MathProblem {
const a = randomInt(2, 7);
const b = randomInt(2, 7);
const diff = Math.abs(a - b);
if (diff === 0) {
return generateDivideInRatio(difficulty);
}
const contexts = [
{ item: "sweets", nameA: "Josh", nameB: "Nathan" },
{ item: "marbles", nameA: "Amy", nameB: "Ben" },
{ item: "stickers", nameA: "Karen", nameB: "Natasha" },
];
const ctx = randomChoice(contexts);
const onePart = randomChoice(difficulty === 1 ? [5, 10, 12] : [6, 8, 9, 15, 20]);
const extraAmount = diff * onePart;
const totalParts = a + b;
const total = totalParts * onePart;
const bigger = a > b ? ctx.nameA : ctx.nameB;
const biggerRatio = Math.max(a, b);
const smallerRatio = Math.min(a, b);
return {
id: nextId(),
prompt: `\\begin{array}{l}\\text{${ctx.item.charAt(0).toUpperCase() + ctx.item.slice(1)} were shared between ${ctx.nameA} and ${ctx.nameB}}\\\\\\text{in the ratio ${a}:${b}. If ${bigger} received ${extraAmount} more ${ctx.item}, find the total shared.}\\end{array}`,
answer: { kind: "integer", value: total },
hints: [
`The difference in ratio parts is ${biggerRatio} - ${smallerRatio} = ${diff}`,
`${diff} parts = ${extraAmount}, so 1 part = ${onePart}`,
`Total parts = ${a} + ${b} = ${totalParts}`,
],
steps: [
{ explanation: `Difference in parts: ${biggerRatio} - ${smallerRatio} = ${diff}` },
{ explanation: `${diff} parts = ${extraAmount}, so 1 part = ${extraAmount} ÷ ${diff} = ${onePart}` },
{ explanation: `Total parts: ${a} + ${b} = ${totalParts}` },
{ explanation: `Total ${ctx.item}: ${totalParts} × ${onePart} = ${total}` },
],
};
}