form 1 topics added
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { gcd } from "./fractions";
|
||||
import { Fraction } from "./fractions";
|
||||
import { simplifyRatio } from "./ratios";
|
||||
|
||||
/** Parse a strict decimal string (digits and optional single dot). Rejects scientific notation, trailing letters, etc. */
|
||||
@@ -29,9 +29,25 @@ export function parseStrictSignedDecimal(input: string): number | null {
|
||||
return parseFloat(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* The outcome of checking one submitted answer.
|
||||
* - `solved` — correct and in the required form (fully done)
|
||||
* - `unsimplified` — correct value, but still needs simplifying (a nudge, not done)
|
||||
* - `wrong` — incorrect; `message` explains why
|
||||
*
|
||||
* A single `status` field means callers branch on one thing, instead of an
|
||||
* error-prone `correct`/`simplified` boolean pair.
|
||||
*/
|
||||
export type AnswerResult =
|
||||
| { correct: true; simplified: boolean }
|
||||
| { correct: false; message: string };
|
||||
| { status: "solved" }
|
||||
| { status: "unsimplified" }
|
||||
| { status: "wrong"; message: string };
|
||||
|
||||
export const SOLVED: AnswerResult = { status: "solved" };
|
||||
export const UNSIMPLIFIED: AnswerResult = { status: "unsimplified" };
|
||||
export function wrong(message: string): AnswerResult {
|
||||
return { status: "wrong", message };
|
||||
}
|
||||
|
||||
export function checkFractionAnswer(
|
||||
userNum: number,
|
||||
@@ -41,28 +57,26 @@ export function checkFractionAnswer(
|
||||
requireSimplified: boolean = true,
|
||||
): AnswerResult {
|
||||
if (userDen === 0) {
|
||||
return { correct: false, message: "Denominator cannot be zero" };
|
||||
return wrong("Denominator cannot be zero");
|
||||
}
|
||||
|
||||
// Check mathematical equivalence
|
||||
const isEquivalent = userNum * expectedDen === expectedNum * userDen;
|
||||
const user = new Fraction(userNum, userDen);
|
||||
const expected = new Fraction(expectedNum, expectedDen);
|
||||
|
||||
if (!isEquivalent) {
|
||||
return { correct: false, message: "That's not quite right. Try again!" };
|
||||
if (!user.equals(expected)) {
|
||||
return wrong("That's not quite right. Try again!");
|
||||
}
|
||||
|
||||
// Any non-zero denominator gives the same value for zero, so don't force 0/1 in the UI.
|
||||
if (userNum === 0) {
|
||||
return { correct: true, simplified: true };
|
||||
if (user.isZero) {
|
||||
return SOLVED;
|
||||
}
|
||||
|
||||
const isSimplified = gcd(Math.abs(userNum), Math.abs(userDen)) === 1;
|
||||
|
||||
if (requireSimplified && !isSimplified) {
|
||||
return { correct: true, simplified: false };
|
||||
if (requireSimplified && !user.isSimplified) {
|
||||
return UNSIMPLIFIED;
|
||||
}
|
||||
|
||||
return { correct: true, simplified: true };
|
||||
return SOLVED;
|
||||
}
|
||||
|
||||
export function checkDecimalAnswer(
|
||||
@@ -71,9 +85,9 @@ export function checkDecimalAnswer(
|
||||
tolerance: number = 0.001,
|
||||
): AnswerResult {
|
||||
if (Math.abs(userValue - expectedValue) <= tolerance) {
|
||||
return { correct: true, simplified: true };
|
||||
return SOLVED;
|
||||
}
|
||||
return { correct: false, message: "That's not quite right. Try again!" };
|
||||
return wrong("That's not quite right. Try again!");
|
||||
}
|
||||
|
||||
export function checkRatioAnswer(
|
||||
@@ -82,34 +96,30 @@ export function checkRatioAnswer(
|
||||
requireSimplified: boolean = true,
|
||||
): AnswerResult {
|
||||
if (userParts.length !== expectedParts.length) {
|
||||
return { correct: false, message: "Check the number of parts in your ratio" };
|
||||
return wrong("Check the number of parts in your ratio");
|
||||
}
|
||||
|
||||
const userSimplified = simplifyRatio(userParts);
|
||||
const expectedSimplified = simplifyRatio(expectedParts);
|
||||
|
||||
const isEquivalent = userSimplified.every((v, i) => v === expectedSimplified[i]);
|
||||
|
||||
if (!isEquivalent) {
|
||||
return { correct: false, message: "That's not quite right. Try again!" };
|
||||
return wrong("That's not quite right. Try again!");
|
||||
}
|
||||
|
||||
if (requireSimplified) {
|
||||
const isAlreadySimplified = userParts.every((v, i) => v === userSimplified[i]);
|
||||
return { correct: true, simplified: isAlreadySimplified };
|
||||
return isAlreadySimplified ? SOLVED : UNSIMPLIFIED;
|
||||
}
|
||||
|
||||
return { correct: true, simplified: true };
|
||||
return SOLVED;
|
||||
}
|
||||
|
||||
export function checkIntegerAnswer(
|
||||
userValue: number,
|
||||
expectedValue: number,
|
||||
): AnswerResult {
|
||||
if (userValue === expectedValue) {
|
||||
return { correct: true, simplified: true };
|
||||
}
|
||||
return { correct: false, message: "That's not quite right. Try again!" };
|
||||
return userValue === expectedValue ? SOLVED : wrong("That's not quite right. Try again!");
|
||||
}
|
||||
|
||||
export function checkOrderingAnswer(
|
||||
@@ -117,11 +127,8 @@ export function checkOrderingAnswer(
|
||||
expectedOrder: number[],
|
||||
): AnswerResult {
|
||||
if (userOrder.length !== expectedOrder.length) {
|
||||
return { correct: false, message: "Make sure you've ordered all the numbers" };
|
||||
return wrong("Make sure you've ordered all the numbers");
|
||||
}
|
||||
const isCorrect = userOrder.every((v, i) => v === expectedOrder[i]);
|
||||
if (isCorrect) {
|
||||
return { correct: true, simplified: true };
|
||||
}
|
||||
return { correct: false, message: "Check your ordering. Try again!" };
|
||||
return isCorrect ? SOLVED : wrong("Check your ordering. Try again!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user