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

193 lines
7.1 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 * as frac from "@/lib/math/fractions";
let counter = 0;
function nextId() {
return `fp-${++counter}-${Date.now()}`;
}
export function generateFractionAddSubtract(difficulty: Difficulty): MathProblem {
const operation = randomChoice(["add", "subtract"] as const);
const op = operation === "add" ? "+" : "-";
let n1: number, d1: number, n2: number, d2: number;
if (difficulty === 1) {
// Common denominators
d1 = randomChoice([2, 3, 4, 5, 6, 8, 10, 12]);
d2 = d1;
n1 = randomInt(1, d1 - 1);
n2 = operation === "subtract" ? randomInt(1, n1) : randomInt(1, d1 - n1);
} else if (difficulty === 2) {
// One denominator is a multiple of the other
d1 = randomChoice([2, 3, 4, 5, 6]);
const multiplier = randomChoice([2, 3]);
d2 = d1 * multiplier;
n1 = randomInt(1, d1 - 1);
n2 = randomInt(1, d2 - 1);
} else {
// Unlike denominators
d1 = randomChoice([3, 4, 5, 7, 8, 9]);
d2 = randomChoice([3, 4, 5, 7, 8, 9].filter((d) => d !== d1));
n1 = randomInt(1, d1 - 1);
n2 = randomInt(1, d2 - 1);
}
const prompt = `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}}`;
const commonDen = frac.lcm(d1, d2);
const convertedN1 = n1 * (commonDen / d1);
const convertedN2 = n2 * (commonDen / d2);
const rawNum = operation === "add"
? convertedN1 + convertedN2
: convertedN1 - convertedN2;
const [ansNum, ansDen] = frac.simplify(rawNum, commonDen);
const steps = d1 === d2
? [
{
explanation: `The denominators already match, so keep ${d1} as the denominator.`,
math: `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}}`,
},
{
explanation: `${operation === "add" ? "Add" : "Subtract"} the numerators and keep the denominator the same.`,
math: `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}} = \\frac{${rawNum}}{${d1}}`,
},
...(ansNum !== rawNum || ansDen !== d1
? [{
explanation: "Simplify the fraction.",
math: `\\frac{${rawNum}}{${d1}} = \\frac{${ansNum}}{${ansDen}}`,
}]
: []),
]
: [
{
explanation: `Find the lowest common denominator: LCM(${d1}, ${d2}) = ${commonDen}.`,
math: `\\text{LCD} = ${commonDen}`,
},
{
explanation: "Rewrite both fractions with that denominator.",
math: `\\frac{${n1}}{${d1}} = \\frac{${convertedN1}}{${commonDen}} \\quad \\frac{${n2}}{${d2}} = \\frac{${convertedN2}}{${commonDen}}`,
},
{
explanation: `${operation === "add" ? "Add" : "Subtract"} the numerators and keep the common denominator.`,
math: `\\frac{${convertedN1}}{${commonDen}} ${op} \\frac{${convertedN2}}{${commonDen}} = \\frac{${rawNum}}{${commonDen}}`,
},
...(ansNum !== rawNum || ansDen !== commonDen
? [{
explanation: "Simplify the fraction.",
math: `\\frac{${rawNum}}{${commonDen}} = \\frac{${ansNum}}{${ansDen}}`,
}]
: []),
];
return {
id: nextId(),
prompt,
answer: { kind: "fraction", numerator: ansNum, denominator: ansDen },
hints: [
d1 === d2 ? "The denominators are already the same!" : "Find a common denominator first",
d1 !== d2 ? "Rewrite both fractions using that denominator" : `Just ${operation} the numerators`,
],
steps,
};
}
export function generateFractionMultiply(difficulty: Difficulty): MathProblem {
const denChoices = difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [2, 3, 4, 5, 6, 7, 8] : [3, 5, 7, 9, 11, 12, 15];
const d1 = randomChoice(denChoices);
const d2 = randomChoice(denChoices);
const n1 = randomInt(1, d1 - 1);
const n2 = randomInt(1, d2 - 1);
const [ansNum, ansDen] = frac.multiply(n1, d1, n2, d2);
return {
id: nextId(),
prompt: `\\frac{${n1}}{${d1}} \\times \\frac{${n2}}{${d2}}`,
answer: { kind: "fraction", numerator: ansNum, denominator: ansDen },
hints: [
"Multiply numerator × numerator",
"Multiply denominator × denominator",
"Simplify if possible",
],
steps: [
{ explanation: `Multiply numerators: ${n1} × ${n2} = ${n1 * n2}` },
{ explanation: `Multiply denominators: ${d1} × ${d2} = ${d1 * d2}` },
{ explanation: "Simplify", math: `\\frac{${ansNum}}{${ansDen}}` },
],
};
}
export function generateFractionDivide(difficulty: Difficulty): MathProblem {
const denChoices = difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [2, 3, 4, 5, 6, 7, 8] : [3, 5, 7, 9, 11, 14, 15];
const d1 = randomChoice(denChoices);
const d2 = randomChoice(denChoices);
const n1 = randomInt(1, d1 - 1);
const n2 = randomInt(1, d2 - 1);
const [ansNum, ansDen] = frac.divide(n1, d1, n2, d2);
return {
id: nextId(),
prompt: `\\frac{${n1}}{${d1}} \\div \\frac{${n2}}{${d2}}`,
answer: { kind: "fraction", numerator: ansNum, denominator: ansDen },
hints: [
"Keep the first fraction the same",
"Flip the second fraction (reciprocal)",
"Then multiply",
],
steps: [
{ explanation: `Keep ${n1}/${d1}, flip ${n2}/${d2} to ${d2}/${n2}` },
{ explanation: `Multiply: ${n1}×${d2} / ${d1}×${n2}` },
{ explanation: "Simplify", math: `\\frac{${ansNum}}{${ansDen}}` },
],
};
}
export function generateFractionOfQuantity(difficulty: Difficulty): MathProblem {
const denChoices = difficulty === 1 ? [2, 4, 5, 10] : difficulty === 2 ? [3, 4, 5, 6, 8] : [7, 8, 9, 12, 15];
const den = randomChoice(denChoices);
const num = randomInt(1, den - 1);
const multiplier = randomChoice(difficulty === 1 ? [2, 4, 5, 10] : [3, 6, 7, 8, 9, 12]);
const quantity = den * multiplier;
const answer = num * multiplier;
return {
id: nextId(),
prompt: `\\frac{${num}}{${den}} \\text{ of } ${quantity}`,
answer: { kind: "integer", value: answer },
hints: [
`Divide ${quantity} by ${den} first`,
`Then multiply by ${num}`,
],
steps: [
{ explanation: `Divide the quantity by the denominator: ${quantity} ÷ ${den} = ${quantity / den}` },
{ explanation: `Multiply by the numerator: ${quantity / den} × ${num} = ${answer}` },
],
};
}
export function generateWholeFromFraction(difficulty: Difficulty): MathProblem {
const denChoices = difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [3, 5, 6, 8] : [7, 8, 9, 12];
const den = randomChoice(denChoices);
const num = randomInt(1, den - 1);
const whole = den * randomChoice(difficulty === 1 ? [2, 3, 4, 5] : [3, 5, 6, 7, 8, 10]);
const part = (num / den) * whole;
return {
id: nextId(),
prompt: `\\frac{${num}}{${den}} \\text{ of a number is } ${part}\\text{. Find the number.}`,
answer: { kind: "integer", value: whole },
hints: [
`If ${num}/${den} of the number is ${part}...`,
`Find 1/${den} first by dividing ${part} by ${num}`,
`Then multiply by ${den} to get the whole`,
],
steps: [
{ explanation: `1/${den} of the number = ${part} ÷ ${num} = ${part / num}` },
{ explanation: `The whole number = ${part / num} × ${den} = ${whole}` },
],
};
}