괄호 앞 음수 분배 스테이지 추가

This commit is contained in:
2026-05-23 12:15:53 +09:00
parent 1ed0611b9d
commit 5ee33bf0d3
13 changed files with 1644 additions and 31 deletions
+118
View File
@@ -0,0 +1,118 @@
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],
};
}