diff --git a/src/algorithms/Recursion/towerOfHanoi.js b/src/algorithms/Recursion/towerOfHanoi.js new file mode 100644 index 0000000..b9fd14a --- /dev/null +++ b/src/algorithms/Recursion/towerOfHanoi.js @@ -0,0 +1,70 @@ +export function towerOfHanoiVisualizerSteps(N) { + const steps = []; + const moves = []; + const rods = { + A: Array.from({ length: N }, (_, i) => N - i), + B: [], + C: [], + }; + + function cloneRods() { + return { + A: [...rods.A], + B: [...rods.B], + C: [...rods.C], + }; + } + + function move(n, from, to, aux) { + if (n === 1) { + const disk = rods[from].pop(); + rods[to].push(disk); + steps.push({ + type: "move", + disk, + from, + to, + rods: cloneRods(), + message: `Move disk ${disk} from ${from} → ${to}`, + }); + moves.push({ disk, from, to }); + return; + } + + steps.push({ + type: "recursive-call", + message: `Move ${n - 1} disks from ${from} → ${aux} using ${to} as auxiliary.`, + rods: cloneRods(), + }); + + move(n - 1, from, aux, to); + + const disk = rods[from].pop(); + rods[to].push(disk); + steps.push({ + type: "move", + disk, + from, + to, + rods: cloneRods(), + message: `Move disk ${disk} from ${from} → ${to}`, + }); + moves.push({ disk, from, to }); + + steps.push({ + type: "recursive-call", + message: `Move ${n - 1} disks from ${aux} → ${to} using ${from} as auxiliary.`, + rods: cloneRods(), + }); + + move(n - 1, aux, to, from); + } + + move(N, "A", "C", "B"); + + return { + steps, + moves, + moveCount: moves.length, + }; +} diff --git a/src/components/Recursion/towerOfHanoiVisualizer.jsx b/src/components/Recursion/towerOfHanoiVisualizer.jsx new file mode 100644 index 0000000..6267df2 --- /dev/null +++ b/src/components/Recursion/towerOfHanoiVisualizer.jsx @@ -0,0 +1,199 @@ +import React, { useState, useEffect, useRef, useCallback } from "react"; +import { towerOfHanoiVisualizerSteps } from "../../algorithms/Recursion/towerOfHanoi"; + +const DEFAULT_N = 3; +const MIN_N = 2; +const MAX_N = 6; +const DEFAULT_SPEED = 700; + +function Rods({ rods }) { + const rodNames = ["A", "B", "C"]; + + return ( +
+ {rodNames.map((rod) => ( +
+
+
+
+ {rods[rod].map((disk, i) => ( +
+ {disk} +
+ ))} +
+ {rod} +
+ ))} +
+ ); +} + +function Legend({ color, text }) { + return ( +
+
+ {text} +
+ ); +} + +export default function TowerOfHanoiVisualizer() { + const [N, setN] = useState(DEFAULT_N); + const [speed, setSpeed] = useState(DEFAULT_SPEED); + const [steps, setSteps] = useState([]); + const [currentStepIdx, setCurrentStepIdx] = useState(0); + const [isPlaying, setIsPlaying] = useState(false); + const [moveCount, setMoveCount] = useState(0); + const timerRef = useRef(null); + + const runSolver = useCallback(() => { + const result = towerOfHanoiVisualizerSteps(N); + setSteps(result.steps); + setMoveCount(result.moveCount); + setCurrentStepIdx(0); + setIsPlaying(false); + }, [N]); + + useEffect(() => { + runSolver(); + }, [runSolver]); + + useEffect(() => { + if (isPlaying && currentStepIdx < steps.length - 1) { + timerRef.current = setTimeout(() => { + setCurrentStepIdx((i) => i + 1); + }, speed); + } else { + clearTimeout(timerRef.current); + setIsPlaying(false); + } + return () => clearTimeout(timerRef.current); + }, [isPlaying, currentStepIdx, steps.length, speed]); + + const step = steps[currentStepIdx] || {}; + const rods = step.rods || { A: [], B: [], C: [] }; + + return ( +
+
+

+ 🏰 Tower of Hanoi Visualizer +

+ + {/* Controls */} +
+
+ + + setN(Math.max(MIN_N, Math.min(MAX_N, Number(e.target.value)))) + } + className="w-20 mt-1 p-2 rounded-md bg-gray-700 text-white border border-gray-600" + /> +
+ +
+ + + setSpeed(Math.max(200, Math.min(2000, Number(e.target.value)))) + } + className="w-20 mt-1 p-2 rounded-md bg-gray-700 text-white border border-gray-600" + /> +
+ + + + + + + + +
+ + {/* Visualization */} +
+
+ +
+ Step {currentStepIdx + 1}/{steps.length} • Total Moves:{" "} + {moveCount} +
+
+ + {/* Info Panel */} +
+
+

+ Step Explanation +

+

+ {step.message || "Solving Tower of Hanoi..."} +

+
+ +
+

+ Color Meanings +

+ + + + +
+
+
+
+
+ ); +} diff --git a/src/pages/Recursion/RecursionPage.jsx b/src/pages/Recursion/RecursionPage.jsx index 8f5bf7d..525f72f 100644 --- a/src/pages/Recursion/RecursionPage.jsx +++ b/src/pages/Recursion/RecursionPage.jsx @@ -3,6 +3,7 @@ import { X, Menu } from "lucide-react"; import MazeSolver from "./MazeSolver"; import NQueens from "./NQueens"; import Sudoku from "./sudokuSolver"; +import TowerOfHanoi from "./towerOfHanoi"; export default function RecursionPage() { const [selectedAlgo, setSelectedAlgo] = useState(""); const [sidebarOpen, setSidebarOpen] = useState(true); @@ -22,11 +23,17 @@ export default function RecursionPage() { ); case "SudokuSolver": - return( + return (
); + case "TowerofHanoi": + return ( +
+ +
+ ); default: return ( @@ -71,6 +78,7 @@ export default function RecursionPage() { +