Skip to content

Commit 7c67738

Browse files
committed
[LEMS-3953/pr5-logarithm-scoring] [Interactive Graph] Add logarithm graph scoring
1 parent 77edce9 commit 7c67738

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

packages/perseus-score/src/widgets/interactive-graph/score-interactive-graph.test.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,122 @@ describe("InteractiveGraph scoring on an exponential question", () => {
617617
});
618618
});
619619

620+
const logarithmRubric: PerseusInteractiveGraphRubric = {
621+
graph: {type: "logarithm"},
622+
correct: {
623+
type: "logarithm",
624+
coords: [
625+
[-4, -3],
626+
[-5, -7],
627+
],
628+
asymptote: -6,
629+
},
630+
};
631+
632+
describe("InteractiveGraph scoring on a logarithm question", () => {
633+
it("marks the answer invalid if guess is undefined", () => {
634+
// Arrange, Act
635+
const result = scoreInteractiveGraph(undefined, logarithmRubric);
636+
637+
// Assert
638+
expect(result).toHaveInvalidInput();
639+
});
640+
641+
it("marks the answer invalid if coords are missing", () => {
642+
// Arrange
643+
const guess: PerseusGraphType = {type: "logarithm"};
644+
645+
// Act
646+
const result = scoreInteractiveGraph(guess, logarithmRubric);
647+
648+
// Assert
649+
expect(result).toHaveInvalidInput();
650+
});
651+
652+
it("marks the answer incorrect if asymptote is missing", () => {
653+
// Arrange — coords present (hasValue=true) but no asymptote,
654+
// so the logarithm scoring block is skipped and falls through to incorrect
655+
const guess: PerseusGraphType = {
656+
type: "logarithm",
657+
coords: [
658+
[-4, -3],
659+
[-5, -7],
660+
],
661+
};
662+
663+
// Act
664+
const result = scoreInteractiveGraph(guess, logarithmRubric);
665+
666+
// Assert
667+
expect(result).toHaveBeenAnsweredIncorrectly();
668+
});
669+
670+
it("marks a correct answer as correct", () => {
671+
// Arrange
672+
const guess: PerseusGraphType = {
673+
type: "logarithm",
674+
coords: [
675+
[-4, -3],
676+
[-5, -7],
677+
],
678+
asymptote: -6,
679+
};
680+
681+
// Act
682+
const result = scoreInteractiveGraph(guess, logarithmRubric);
683+
684+
// Assert
685+
expect(result).toHaveBeenAnsweredCorrectly();
686+
});
687+
688+
it("marks a wrong answer as incorrect", () => {
689+
// Arrange — different curve
690+
const guess: PerseusGraphType = {
691+
type: "logarithm",
692+
coords: [
693+
[2, 1],
694+
[3, 2],
695+
],
696+
asymptote: 0,
697+
};
698+
699+
// Act
700+
const result = scoreInteractiveGraph(guess, logarithmRubric);
701+
702+
// Assert
703+
expect(result).toHaveBeenAnsweredIncorrectly();
704+
});
705+
706+
it("marks equivalent curves with different control points as correct", () => {
707+
// Arrange — y = ln(x): both sets of points produce a=1, b=1, c=0
708+
const lnRubric: PerseusInteractiveGraphRubric = {
709+
graph: {type: "logarithm"},
710+
correct: {
711+
type: "logarithm",
712+
coords: [
713+
[1, 0],
714+
[Math.E, 1],
715+
],
716+
asymptote: 0,
717+
},
718+
};
719+
const guess: PerseusGraphType = {
720+
type: "logarithm",
721+
coords: [
722+
[Math.E * Math.E, 2],
723+
[Math.E * Math.E * Math.E, 3],
724+
],
725+
asymptote: 0,
726+
};
727+
728+
// Act
729+
const result = scoreInteractiveGraph(guess, lnRubric);
730+
731+
// Assert
732+
expect(result).toHaveBeenAnsweredCorrectly();
733+
});
734+
});
735+
620736
describe("InteractiveGraph scoring on an absolute-value question", () => {
621737
it("marks the answer invalid if guess is undefined", () => {
622738
// Arrange

packages/perseus-score/src/widgets/interactive-graph/score-interactive-graph.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const {
3131
getSinusoidCoefficients,
3232
getQuadraticCoefficients,
3333
getExponentialCoefficients,
34+
getLogarithmCoefficients,
3435
getTangentCoefficients,
3536
} = coefficients;
3637

@@ -185,6 +186,35 @@ function scoreInteractiveGraph(
185186
message: null,
186187
};
187188
}
189+
} else if (
190+
userInput.type === "logarithm" &&
191+
rubric.correct.type === "logarithm" &&
192+
userInput.coords != null &&
193+
userInput.asymptote != null
194+
) {
195+
const guessCoeffs = getLogarithmCoefficients(
196+
userInput.coords,
197+
userInput.asymptote,
198+
);
199+
const correctCoeffs = getLogarithmCoefficients(
200+
rubric.correct.coords,
201+
rubric.correct.asymptote,
202+
);
203+
if (
204+
guessCoeffs != null &&
205+
correctCoeffs != null &&
206+
approximateDeepEqual(
207+
[guessCoeffs.a, guessCoeffs.b, guessCoeffs.c],
208+
[correctCoeffs.a, correctCoeffs.b, correctCoeffs.c],
209+
)
210+
) {
211+
return {
212+
type: "points",
213+
earned: 1,
214+
total: 1,
215+
message: null,
216+
};
217+
}
188218
} else if (
189219
userInput.type === "absolute-value" &&
190220
rubric.correct.type === "absolute-value" &&

0 commit comments

Comments
 (0)