form 1 topics added

This commit is contained in:
2026-07-05 11:12:45 -04:00
parent a614532810
commit a23fa53772
58 changed files with 7089 additions and 475 deletions

View File

@@ -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!");
}