119 lines
2.8 KiB
TypeScript
119 lines
2.8 KiB
TypeScript
import type {
|
|
GradeResult,
|
|
ParenthesisSignProblem,
|
|
UserAnswer,
|
|
} from "./types";
|
|
|
|
const MIN_MULTIPLIER = -9;
|
|
const MAX_MULTIPLIER = -1;
|
|
const MIN_TERM = 1;
|
|
const MAX_TERM = 9;
|
|
const MAX_PROBLEM_COUNT = 9 * 8 * 2;
|
|
|
|
type ProblemSeed = Pick<
|
|
ParenthesisSignProblem,
|
|
"multiplier" | "a" | "operator" | "b"
|
|
>;
|
|
|
|
function randomInt(min: number, max: number) {
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
}
|
|
|
|
function formatLatexTermPair(first: number, second: number) {
|
|
const operator = second < 0 ? "-" : "+";
|
|
|
|
return `${first} ${operator} ${Math.abs(second)}`;
|
|
}
|
|
|
|
function createProblem({ multiplier, a, operator, b }: ProblemSeed) {
|
|
const distributedA = multiplier * a;
|
|
const distributedB = operator === "-" ? multiplier * -b : multiplier * b;
|
|
const key = `${multiplier}:${a}:${operator}:${b}`;
|
|
|
|
return {
|
|
id: `parenthesis-sign:${key}`,
|
|
key,
|
|
multiplier,
|
|
a,
|
|
operator,
|
|
b,
|
|
distributedA,
|
|
distributedB,
|
|
latexBefore: `${multiplier}\\left(${a} ${operator} ${b}\\right)`,
|
|
latexAfter: formatLatexTermPair(distributedA, distributedB),
|
|
};
|
|
}
|
|
|
|
function createAllProblemSeeds() {
|
|
const seeds: ProblemSeed[] = [];
|
|
|
|
for (
|
|
let multiplier = MIN_MULTIPLIER;
|
|
multiplier <= MAX_MULTIPLIER;
|
|
multiplier += 1
|
|
) {
|
|
for (let a = MIN_TERM; a <= MAX_TERM; a += 1) {
|
|
for (let b = MIN_TERM; b <= MAX_TERM; b += 1) {
|
|
if (a === b) continue;
|
|
|
|
seeds.push({ multiplier, a, operator: "+", b });
|
|
seeds.push({ multiplier, a, operator: "-", b });
|
|
}
|
|
}
|
|
}
|
|
|
|
return seeds;
|
|
}
|
|
|
|
function shuffle<T>(items: T[]) {
|
|
const shuffled = [...items];
|
|
|
|
for (let index = shuffled.length - 1; index > 0; index -= 1) {
|
|
const targetIndex = randomInt(0, index);
|
|
const current = shuffled[index];
|
|
shuffled[index] = shuffled[targetIndex];
|
|
shuffled[targetIndex] = current;
|
|
}
|
|
|
|
return shuffled;
|
|
}
|
|
|
|
export function generateProblem(): ParenthesisSignProblem {
|
|
const a = randomInt(MIN_TERM, MAX_TERM);
|
|
let b = randomInt(MIN_TERM, MAX_TERM);
|
|
|
|
while (a === b) {
|
|
b = randomInt(MIN_TERM, MAX_TERM);
|
|
}
|
|
|
|
return createProblem({
|
|
multiplier: randomInt(MIN_MULTIPLIER, MAX_MULTIPLIER),
|
|
a,
|
|
operator: Math.random() < 0.5 ? "+" : "-",
|
|
b,
|
|
});
|
|
}
|
|
|
|
export function generateSession(count = 10): ParenthesisSignProblem[] {
|
|
if (!Number.isInteger(count) || count < 1) {
|
|
throw new Error("count must be a positive integer.");
|
|
}
|
|
|
|
if (count > MAX_PROBLEM_COUNT) {
|
|
throw new Error(`count cannot exceed ${MAX_PROBLEM_COUNT}.`);
|
|
}
|
|
|
|
return shuffle(createAllProblemSeeds()).slice(0, count).map(createProblem);
|
|
}
|
|
|
|
export function gradeAnswer(
|
|
problem: ParenthesisSignProblem,
|
|
answer: UserAnswer,
|
|
): GradeResult {
|
|
return {
|
|
correct:
|
|
answer.a === problem.distributedA && answer.b === problem.distributedB,
|
|
correctAnswer: [problem.distributedA, problem.distributedB],
|
|
};
|
|
}
|