"use client";
import { useState, useCallback } from "react";
import { Card } from "@/components/ui/card";
import { StepControls } from "./step-controls";
import { MathDisplay } from "@/components/math/math-display";
type Mode = "number-line" | "rules";
interface Step {
label: string;
math: string;
numberLineHighlight?: {
start: number;
end: number;
direction: "left" | "right";
moves: number;
};
}
function buildAddSubtractSteps(a: number, b: number, op: "add" | "subtract"): Step[] {
const steps: Step[] = [];
const opSymbol = op === "add" ? "+" : "-";
const expression = `${a} ${opSymbol} ${b < 0 ? `(${b})` : b}`;
// Step 0: Show the original expression
steps.push({
label: "Start with the expression",
math: expression,
numberLineHighlight: { start: a, end: a, direction: "right", moves: 0 },
});
if (op === "add") {
// Adding
steps.push({
label: `Place your finger on ${a} on the number line`,
math: `\\text{Start at } ${a}`,
numberLineHighlight: { start: a, end: a, direction: "right", moves: 0 },
});
if (b >= 0) {
steps.push({
label: `Adding a positive number: move ${b} steps to the RIGHT`,
math: `${a} + ${b} \\longrightarrow \\text{move right } ${b}`,
numberLineHighlight: { start: a, end: a + b, direction: "right", moves: b },
});
} else {
steps.push({
label: `Adding a negative number: move ${Math.abs(b)} steps to the LEFT`,
math: `${a} + (${b}) \\longrightarrow \\text{move left } ${Math.abs(b)}`,
numberLineHighlight: { start: a, end: a + b, direction: "left", moves: Math.abs(b) },
});
}
const result = a + b;
steps.push({
label: `You land on ${result}`,
math: `${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: b >= 0 ? "right" : "left", moves: Math.abs(b) },
});
// Sign rule explanation
if (a >= 0 && b >= 0) {
steps.push({
label: "Rule: Positive + Positive = Positive",
math: `(+) + (+) = (+) \\quad \\Rightarrow \\quad ${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: "right", moves: Math.abs(b) },
});
} else if (a < 0 && b < 0) {
steps.push({
label: "Rule: Negative + Negative = Negative (add magnitudes, keep negative sign)",
math: `(-) + (-) = (-) \\quad \\Rightarrow \\quad ${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: "left", moves: Math.abs(b) },
});
} else {
steps.push({
label: "Rule: Different signs? Subtract the smaller from the larger, keep the sign of the larger",
math: `|${Math.abs(a)}| - |${Math.abs(b)}| = ${Math.abs(Math.abs(a) - Math.abs(b))} \\quad \\Rightarrow \\quad ${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: result >= a ? "right" : "left", moves: Math.abs(b) },
});
}
} else {
// Subtracting
steps.push({
label: `Place your finger on ${a} on the number line`,
math: `\\text{Start at } ${a}`,
numberLineHighlight: { start: a, end: a, direction: "right", moves: 0 },
});
if (b >= 0) {
steps.push({
label: `Subtracting a positive number: move ${b} steps to the LEFT`,
math: `${a} - ${b} \\longrightarrow \\text{move left } ${b}`,
numberLineHighlight: { start: a, end: a - b, direction: "left", moves: b },
});
} else {
steps.push({
label: `Subtracting a negative number is the same as ADDING a positive! Move ${Math.abs(b)} steps RIGHT`,
math: `${a} - (${b}) = ${a} + ${Math.abs(b)} \\longrightarrow \\text{move right } ${Math.abs(b)}`,
numberLineHighlight: { start: a, end: a - b, direction: "right", moves: Math.abs(b) },
});
}
const result = a - b;
steps.push({
label: `You land on ${result}`,
math: `${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: result >= a ? "right" : "left", moves: Math.abs(b) },
});
// Two like/unlike signs rule
if (b >= 0) {
steps.push({
label: "Two unlike signs (+ -) becomes negative: subtract means move left",
math: `+(-)\\text{ becomes } - \\quad \\Rightarrow \\quad ${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: "left", moves: Math.abs(b) },
});
} else {
steps.push({
label: "Two like signs (- -) becomes positive: subtracting a negative means move right!",
math: `-(-) \\text{ becomes } + \\quad \\Rightarrow \\quad ${expression} = ${result}`,
numberLineHighlight: { start: a, end: result, direction: "right", moves: Math.abs(b) },
});
}
}
return steps;
}
function NumberLine({
highlight,
animate,
}: {
highlight?: Step["numberLineHighlight"];
animate: boolean;
}) {
const minVal = -15;
const maxVal = 15;
const range = maxVal - minVal;
const start = highlight?.start ?? 0;
const end = highlight?.end ?? 0;
const startX = ((start - minVal) / range) * 100;
const endX = ((end - minVal) / range) * 100;
const leftX = Math.min(startX, endX);
const width = Math.abs(endX - startX);
return (
{/* Arrow path highlight */}
{highlight && highlight.moves > 0 && (
)}
{/* Direction arrow */}
{highlight && highlight.moves > 0 && (
{highlight.direction === "right" ? "→ right →" : "← left ←"}
{highlight.moves} step{highlight.moves !== 1 ? "s" : ""}
)}
{/* Number line */}
{/* Tick marks */}
{Array.from({ length: range + 1 }, (_, i) => {
const val = minVal + i;
const x = (i / range) * 100;
const isZero = val === 0;
const isHighlighted = highlight && (val === start || val === end);
const isInRange = highlight && highlight.moves > 0 &&
val >= Math.min(start, end) && val <= Math.max(start, end);
return (
{(val % 5 === 0 || isHighlighted) && (
{val}
)}
);
})}
{/* Start marker */}
{highlight && (
)}
{/* End marker */}
{highlight && highlight.moves > 0 && animate && (
)}
{/* Arrows on edges */}
← negative
positive →
);
}
function SignRuleCard({ rule, active }: { rule: string; active: boolean }) {
return (
{rule}
);
}
function QuickPractice() {
const [problem, setProblem] = useState(() => generateProblem());
const [userAnswer, setUserAnswer] = useState("");
const [feedback, setFeedback] = useState<"correct" | "incorrect" | null>(null);
const [score, setScore] = useState({ correct: 0, total: 0 });
function generateProblem() {
const ops = ["+", "-"] as const;
const op = ops[Math.floor(Math.random() * ops.length)];
const a = Math.floor(Math.random() * 21) - 10;
const b = Math.floor(Math.random() * 21) - 10;
const answer = op === "+" ? a + b : a - b;
return { a, b, op, answer };
}
function checkAnswer() {
const parsed = parseInt(userAnswer);
if (isNaN(parsed)) return;
const isCorrect = parsed === problem.answer;
setFeedback(isCorrect ? "correct" : "incorrect");
setScore((prev) => ({
correct: prev.correct + (isCorrect ? 1 : 0),
total: prev.total + 1,
}));
}
function nextProblem() {
setProblem(generateProblem());
setUserAnswer("");
setFeedback(null);
}
const displayB = problem.b < 0 ? `(${problem.b})` : `${problem.b}`;
return (
Quick Practice
{score.correct}/{score.total}
{problem.a} {problem.op} {displayB} =
{
setUserAnswer(e.target.value);
setFeedback(null);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
if (feedback !== null) nextProblem();
else checkAnswer();
}
}}
className={`w-20 rounded-lg border-2 px-3 py-2 text-center text-xl font-bold outline-none transition-colors ${
feedback === "correct"
? "border-correct bg-correct-light"
: feedback === "incorrect"
? "border-incorrect bg-incorrect-light"
: "border-border focus:border-unit-5"
}`}
placeholder="?"
aria-label="Your answer"
/>
{feedback === null ? (
) : (
)}
{feedback === "correct" && (
Correct! Well done!
)}
{feedback === "incorrect" && (
Not quite. The answer is {problem.answer}. Try the next one!
)}
);
}
export function IntegerAddSubtractExplorer() {
const [mode, setMode] = useState("number-line");
const [op, setOp] = useState<"add" | "subtract">("add");
const [inputA, setInputA] = useState("-3");
const [inputB, setInputB] = useState("5");
const [error, setError] = useState("");
const [steps, setSteps] = useState(null);
const [currentStep, setCurrentStep] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const done = steps ? currentStep >= steps.length - 1 : false;
function handleGo() {
setError("");
const a = parseInt(inputA);
const b = parseInt(inputB);
if (isNaN(a) || isNaN(b)) {
setError("Enter valid integers.");
return;
}
if (Math.abs(a) > 15 || Math.abs(b) > 15) {
setError("Keep numbers between -15 and 15 for the number line.");
return;
}
const result = op === "add" ? a + b : a - b;
if (Math.abs(result) > 15) {
setError("Result goes off the number line. Try smaller numbers.");
return;
}
try {
const s = buildAddSubtractSteps(a, b, op);
setSteps(s);
setCurrentStep(0);
setIsPlaying(false);
} catch {
setError("Could not compute. Check your inputs.");
}
}
const stepForward = useCallback(() => {
if (!steps || currentStep >= steps.length - 1) return;
setCurrentStep((s) => s + 1);
if (currentStep + 1 >= steps.length - 1) setIsPlaying(false);
}, [steps, currentStep]);
const stepBack = useCallback(() => {
if (currentStep <= 0) return;
setCurrentStep((s) => s - 1);
}, [currentStep]);
const togglePlay = useCallback(() => setIsPlaying((p) => !p), []);
const reset = useCallback(() => {
setSteps(null);
setCurrentStep(0);
setIsPlaying(false);
}, []);
const step = steps ? steps[currentStep] : null;
const addRules = [
{ rule: "(+) + (+) = (+)", active: step?.label.includes("Positive + Positive") ?? false },
{ rule: "(-) + (-) = (-)", active: step?.label.includes("Negative + Negative") ?? false },
{ rule: "Different signs? Subtract, keep sign of larger", active: step?.label.includes("Different signs") ?? false },
];
const subRules = [
{ rule: "+(−) becomes −", active: step?.label.includes("unlike signs") ?? false },
{ rule: "−(−) becomes +", active: step?.label.includes("like signs") ?? false },
];
return (
{/* Mode toggle */}
{(["number-line", "rules"] as Mode[]).map((m) => (
))}
{/* Operation tabs */}
{(["add", "subtract"] as const).map((o) => (
))}
{/* Input Card */}
Enter two integers
setInputA(e.target.value)}
className="w-20 rounded-lg border-2 border-border bg-surface px-3 py-2 text-center text-lg font-bold outline-none focus:border-unit-5"
aria-label="First integer"
/>
{op === "add" ? "+" : "−"}
setInputB(e.target.value)}
className="w-20 rounded-lg border-2 border-border bg-surface px-3 py-2 text-center text-lg font-bold outline-none focus:border-unit-5"
aria-label="Second integer"
/>
{error && {error}
}
{/* Sign Rules Reference */}
{mode === "rules" && (
{(op === "add" ? addRules : subRules).map((r) => (
))}
)}
{/* Display Card with Number Line */}
{!step ? (
Enter integers above and click Go
) : (
<>
{step.label}
{mode === "number-line" && (
0}
/>
)}
>
)}
{/* Controls */}
0}
/>
{/* Result */}
{!done || !steps ? (
Result will appear here when steps are complete
) : (
<>
Answer
>
)}
{/* Quick Practice */}
);
}