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

105 lines
3.6 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 } from "@/lib/utils";
let counter = 0;
function nextId() {
return `ns-${++counter}-${Date.now()}`;
}
export function generateBinaryToDenary(difficulty: Difficulty): MathProblem {
const bits = difficulty === 1 ? randomInt(3, 4) : difficulty === 2 ? randomInt(5, 6) : randomInt(7, 8);
// ensure a leading 1 so the length is meaningful
let binary = "1";
for (let i = 1; i < bits; i++) binary += Math.random() < 0.5 ? "0" : "1";
const len = binary.length;
let value = 0;
const parts: string[] = [];
for (let i = 0; i < len; i++) {
const exp = len - 1 - i;
const digit = parseInt(binary[i], 10);
const place = Math.pow(2, exp);
if (digit === 1) parts.push(`${place}`);
value += digit * place;
}
return {
id: nextId(),
prompt: `${binary}_{2} = ?_{10}`,
answer: { kind: "integer", value },
hints: [
"Each column is a power of 2, doubling from the right: 1, 2, 4, 8, 16 …",
"Add up the place values wherever there is a 1.",
`${parts.join(" + ")} = ${value}`,
],
steps: [
{ explanation: "Label each column with its power of 2, starting at 1 on the right." },
{ explanation: `Add the place values under each 1: ${parts.join(" + ")}` },
{ explanation: `${binary} in binary = ${value} in denary.` },
],
};
}
export function generateQuaternaryToDenary(difficulty: Difficulty): MathProblem {
const digits = difficulty === 1 ? randomInt(2, 3) : difficulty === 2 ? randomInt(3, 4) : randomInt(4, 5);
let quaternary = `${randomInt(1, 3)}`; // leading non-zero digit
for (let i = 1; i < digits; i++) quaternary += `${randomInt(0, 3)}`;
const len = quaternary.length;
let value = 0;
const parts: string[] = [];
for (let i = 0; i < len; i++) {
const exp = len - 1 - i;
const digit = parseInt(quaternary[i], 10);
const place = Math.pow(4, exp);
if (digit !== 0) parts.push(`${digit}×${place}`);
value += digit * place;
}
return {
id: nextId(),
prompt: `${quaternary}_{4} = ?_{10}`,
answer: { kind: "integer", value },
hints: [
"Each column is a power of 4, from the right: 1, 4, 16, 64 …",
"Multiply each digit by its place value and add them up.",
`${parts.join(" + ")} = ${value}`,
],
steps: [
{ explanation: "Label each column with its power of 4, starting at 1 on the right." },
{ explanation: `Add the products: ${parts.join(" + ")}` },
{ explanation: `${quaternary} in base 4 = ${value} in denary.` },
],
};
}
export function generateDigitValue(difficulty: Difficulty): MathProblem {
const digits = difficulty === 1 ? randomInt(3, 4) : difficulty === 2 ? 5 : 6;
const min = Math.pow(10, digits - 1);
const max = Math.pow(10, digits) - 1;
const n = randomInt(min, max);
const str = `${n}`;
// pick a non-zero digit position
const nonZeroPositions = str.split("").map((d, i) => ({ d, i })).filter((x) => x.d !== "0");
const pick = nonZeroPositions[randomInt(0, nonZeroPositions.length - 1)];
const exp = str.length - 1 - pick.i;
const place = Math.pow(10, exp);
const value = parseInt(pick.d, 10) * place;
return {
id: nextId(),
prompt: `\\text{In } ${n} \\text{, what is the value of the digit } ${pick.d}\\text{?}`,
answer: { kind: "integer", value },
hints: [
"Find which column the digit sits in.",
`That column is worth 10^${exp} = ${place}.`,
`${pick.d} × ${place} = ${value}`,
],
steps: [
{ explanation: `The digit ${pick.d} is in the 10^${exp} (${place}) column.` },
{ explanation: `Its value is ${pick.d} × ${place} = ${value}.` },
],
};
}