Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lazy-bats-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus-score": minor
---

Added ability to score Vector Interactive Graphs
Original file line number Diff line number Diff line change
Expand Up @@ -932,3 +932,104 @@ describe("InteractiveGraph scoring on a tangent question", () => {
expect(result).toHaveBeenAnsweredCorrectly();
});
});

const vectorRubric: PerseusInteractiveGraphRubric = {
graph: {type: "vector"},
correct: {
type: "vector",
coords: [
[0, 0],
[3, 4],
],
},
};

describe("InteractiveGraph scoring on a vector question", () => {
it("marks the answer invalid if guess is undefined", () => {
// Arrange, Act
const result = scoreInteractiveGraph(undefined, vectorRubric);

// Assert
expect(result).toHaveInvalidInput();
});

it("marks the answer invalid if coords are missing", () => {
// Arrange
const guess: PerseusGraphType = {type: "vector"};

// Act
const result = scoreInteractiveGraph(guess, vectorRubric);

// Assert
expect(result).toHaveInvalidInput();
});

it("marks a correct answer as correct", () => {
// Arrange
const guess: PerseusGraphType = {
type: "vector",
coords: [
[0, 0],
[3, 4],
],
};

// Act
const result = scoreInteractiveGraph(guess, vectorRubric);

// Assert
expect(result).toHaveBeenAnsweredCorrectly();
});

it("marks a wrong tail as incorrect", () => {
// Arrange — correct tip but wrong tail
const guess: PerseusGraphType = {
type: "vector",
coords: [
[1, 1],
[3, 4],
],
};

// Act
const result = scoreInteractiveGraph(guess, vectorRubric);

// Assert
expect(result).toHaveBeenAnsweredIncorrectly();
});

it("marks a wrong tip as incorrect", () => {
// Arrange — correct tail but wrong tip
const guess: PerseusGraphType = {
type: "vector",
coords: [
[0, 0],
[4, 5],
],
};

// Act
const result = scoreInteractiveGraph(guess, vectorRubric);

// Assert
expect(result).toHaveBeenAnsweredIncorrectly();
});

it("marks swapped tail and tip as incorrect", () => {
// Arrange — coords are reversed (tip at tail position, tail at tip)
// Unlike ray, vector scoring is order-sensitive
const guess: PerseusGraphType = {
type: "vector",
coords: [
[3, 4],
[0, 0],
],
};

// Act
const result = scoreInteractiveGraph(guess, vectorRubric);

// Assert
expect(result).toHaveBeenAnsweredIncorrectly();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,31 @@ function scoreInteractiveGraph(
message: null,
};
}
} else if (
userInput.type === "vector" &&
rubric.correct.type === "vector" &&
userInput.coords != null &&
rubric.correct.coords != null
) {
// Vector scoring: both tail and tip must match exactly.
// Order matters — coords[0] is tail, coords[1] is tip.
if (
approximateDeepEqual(
userInput.coords[0],
rubric.correct.coords[0],
) &&
approximateDeepEqual(
userInput.coords[1],
rubric.correct.coords[1],
)
) {
return {
type: "points",
earned: 1,
total: 1,
message: null,
};
}
}
}

Expand Down
Loading