Files
cabrits-math/lib/problems/generators/fraction-problems.ts
2026-03-01 18:50:29 -04:00

164 lines
6.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 [ansNum, ansDen] = operation === "add"
? frac.add(n1, d1, n2, d2)
: frac.subtract(n1, d1, n2, d2);
const prompt = `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}}`;
const commonDen = frac.lcm(d1, d2);
const steps = d1 === d2
? [
{ explanation: "Denominators are the same, so just " + (operation === "add" ? "add" : "subtract") + " the numerators" },
{ explanation: `${n1} ${op} ${n2} = ${operation === "add" ? n1 + n2 : n1 - n2}`, math: `\\frac{${operation === "add" ? n1 + n2 : n1 - n2}}{${d1}}` },
]
: [
{ explanation: `Find the common denominator: LCM(${d1}, ${d2}) = ${commonDen}` },
{ explanation: `Convert: ${n1}×${commonDen / d1}/${d1}×${commonDen / d1} and ${n2}×${commonDen / d2}/${d2}×${commonDen / d2}` },
{ explanation: `${op === "+" ? "Add" : "Subtract"} numerators`, math: `\\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 ? `Try using the Butterfly Method: cross multiply` : `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}` },
],
};
}