Skip to content

Commit cba7dd2

Browse files
committed
Start refactoring out Hooks from Pathfinding Visualiser
* Simplifying codebase before templating out algorithm / maze generation
1 parent d24f589 commit cba7dd2

File tree

3 files changed

+131
-94
lines changed

3 files changed

+131
-94
lines changed

src/pages/PathfindingVisualiser.tsx

Lines changed: 16 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ import { Dijkstra } from '../algorithms/pathfinding/Dijkstra.ts';
99
import { DepthFirstSearch } from '../algorithms/pathfinding/DepthFirstSearch.ts';
1010
import { primsMazeGeneration } from '../algorithms/mazes/PrimsMazeGeneration.ts';
1111
import { dfsMazeGeneration } from '../algorithms/mazes/DFSMazeGeneration.ts';
12+
import mazeHooks from '../utils/mazeHooks.ts';
1213

1314
const PathfindingVisualiser: React.FC = () => {
1415
const { width = 0, height = 0 } = useWindowDimensions();
15-
const [maze, setMaze] = useState<Cell[][]>([]);
1616
const [isDrawing, setIsDrawing] = useState<boolean>(false);
1717
const [drawingValue, setDrawingValue] = useState<boolean>(false);
18-
const [startNode, setStartNode] = useState<number[]>([0, 0]);
19-
const [endNode, setEndNode] = useState<number[]>([0, 0]);
2018
const [isMovingNode, setIsMovingNode] = useState<'start' | 'end' | null>(null);
2119
const [runDijkstra, setRunDijkstra] = useState<boolean>(false);
2220
const [runBFS, setRunBFS] = useState<boolean>(false);
@@ -63,48 +61,17 @@ const PathfindingVisualiser: React.FC = () => {
6361
}
6462
}, [generateDFSMaze]);
6563

66-
function setStartPosition(flatHeight: number, flatWidth: number){
67-
const rowIndex = Math.floor(flatHeight / 2);
68-
const colIndex = Math.floor(flatWidth / 4);
69-
setMaze(prevMaze => {
70-
const newMaze = [...prevMaze];
71-
newMaze[rowIndex][colIndex] = new Cell(rowIndex, colIndex);
72-
newMaze[rowIndex][colIndex].start = true;
73-
return newMaze;
74-
});
75-
setStartNode([rowIndex, colIndex]);
76-
}
77-
78-
function setEndPosition(flatHeight: number, flatWidth: number){
79-
const rowIndex = Math.floor(flatHeight / 2);
80-
const colIndex = Math.floor(flatWidth * 3 / 4);
81-
setMaze(prevMaze => {
82-
const newMaze = [...prevMaze];
83-
newMaze[rowIndex][colIndex] = new Cell(rowIndex, colIndex);
84-
newMaze[rowIndex][colIndex].end = true;
85-
return newMaze;
86-
});
87-
setEndNode([rowIndex, colIndex]);
88-
}
89-
90-
function generateTable() {
91-
let flatHeight = Math.floor((height - 26) / 26);
92-
if (flatHeight % 2 === 0) flatHeight += 1;
93-
let flatWidth = Math.floor((width - 326) / 26);
94-
if (flatWidth % 2 === 0) flatWidth += 1;
95-
96-
const newMaze = [];
97-
for (let i = 0; i < flatHeight; i++) {
98-
const row = [];
99-
for (let j = 0; j < flatWidth; j++) {
100-
row.push(new Cell(i, j));
101-
}
102-
newMaze.push(row);
103-
}
104-
setMaze(newMaze);
105-
setStartPosition(flatHeight, flatWidth);
106-
setEndPosition(flatHeight, flatWidth);
107-
}
64+
const {
65+
maze,
66+
setMaze,
67+
startNode,
68+
endNode,
69+
setStartNode,
70+
setEndNode,
71+
generateTable,
72+
clearAttribute,
73+
populateOptimalPath,
74+
} = mazeHooks(width, height);
10875

10976
const handleMouseDown = (rowIndex: number, colIndex: number) => {
11077
if (maze[rowIndex][colIndex].start) {
@@ -158,50 +125,6 @@ const PathfindingVisualiser: React.FC = () => {
158125
});
159126
};
160127

161-
function clearAttribute(attribute: keyof Cell) {
162-
if (maze.length === 0 || maze[0].length === 0) {
163-
console.error(`Maze is empty or not initialized.`);
164-
return;
165-
}
166-
167-
const sampleCell = maze[0][0];
168-
const validAttributes = Object.keys(sampleCell);
169-
170-
if (!validAttributes.includes(attribute)) {
171-
console.error(`Invalid attribute: ${attribute}`);
172-
return;
173-
}
174-
175-
setMaze(prevMaze => prevMaze.map(row =>
176-
row.map(cell => ({ ...cell, [attribute]: false }))
177-
));
178-
};
179-
180-
async function populateOptimalPath(optimalPath: Cell[] | null) {
181-
if (!optimalPath) {
182-
return;
183-
}
184-
185-
const delay = 50; // Delay in milliseconds between each update
186-
187-
const updateCell = (index: number) => {
188-
if (index >= optimalPath.length) {
189-
return;
190-
}
191-
192-
setMaze(prevMaze => {
193-
const newMaze = prevMaze.map(row => row.map(cell => ({ ...cell })));
194-
newMaze[optimalPath[index].row][optimalPath[index].col].finalPath = true;
195-
return newMaze;
196-
});
197-
198-
// Schedule the next update with delay
199-
requestAnimationFrame(() => setTimeout(() => updateCell(index + 1), delay));
200-
};
201-
202-
updateCell(0);
203-
}
204-
205128
async function callBFS(){
206129
const optimalPath = await BreadthFirstSearch(maze, maze[startNode[0]][startNode[1]], maze[endNode[0]][endNode[1]], setMaze);
207130
await populateOptimalPath(optimalPath);
@@ -235,23 +158,23 @@ const PathfindingVisualiser: React.FC = () => {
235158
setRunDFS(true);
236159
}
237160

238-
function primsMazeRun(){
161+
function triggerPrimsMaze(){
239162
clearAttribute('visited');
240163
clearAttribute('finalPath');
241164
setGeneratePrimsMaze(true);
242165
}
243166

244-
function DFSMazeRun(){
167+
function triggerDFSMaze(){
245168
clearAttribute('visited');
246169
clearAttribute('finalPath');
247170
setGenerateDFSMaze(true);
248171
}
249172

250-
function triggerPrimsMaze(){
173+
function primsMazeRun(){
251174
primsMazeGeneration(maze, maze[startNode[0]][startNode[1]], maze[endNode[0]][endNode[1]], setMaze);
252175
}
253176

254-
function triggerDFSMaze(){
177+
function DFSMazeRun(){
255178
dfsMazeGeneration(maze, maze[startNode[0]][startNode[1]], maze[endNode[0]][endNode[1]], setMaze);
256179
}
257180

src/utils/mazeHooks.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// useMaze.ts
2+
import { useState, useEffect } from 'react';
3+
import { Cell } from '../algorithms/utils/PathfindingUtils';
4+
5+
const mazeHooks = (width: number, height: number) => {
6+
const [maze, setMaze] = useState<Cell[][]>([]);
7+
const [startNode, setStartNode] = useState<number[]>([0, 0]);
8+
const [endNode, setEndNode] = useState<number[]>([0, 0]);
9+
10+
useEffect(() => {
11+
generateTable();
12+
}, [width, height]);
13+
14+
function setStartPosition(flatHeight: number, flatWidth: number){
15+
const rowIndex = Math.floor(flatHeight / 2);
16+
const colIndex = Math.floor(flatWidth / 4);
17+
setMaze(prevMaze => {
18+
const newMaze = [...prevMaze];
19+
newMaze[rowIndex][colIndex] = new Cell(rowIndex, colIndex);
20+
newMaze[rowIndex][colIndex].start = true;
21+
return newMaze;
22+
});
23+
setStartNode([rowIndex, colIndex]);
24+
}
25+
26+
function setEndPosition(flatHeight: number, flatWidth: number){
27+
const rowIndex = Math.floor(flatHeight / 2);
28+
const colIndex = Math.floor(flatWidth * 3 / 4);
29+
setMaze(prevMaze => {
30+
const newMaze = [...prevMaze];
31+
newMaze[rowIndex][colIndex] = new Cell(rowIndex, colIndex);
32+
newMaze[rowIndex][colIndex].end = true;
33+
return newMaze;
34+
});
35+
setEndNode([rowIndex, colIndex]);
36+
}
37+
38+
function generateTable() {
39+
let flatHeight = Math.floor((height - 26) / 26);
40+
if (flatHeight % 2 === 0) flatHeight += 1;
41+
let flatWidth = Math.floor((width - 326) / 26);
42+
if (flatWidth % 2 === 0) flatWidth += 1;
43+
44+
const newMaze = [];
45+
for (let i = 0; i < flatHeight; i++) {
46+
const row = [];
47+
for (let j = 0; j < flatWidth; j++) {
48+
row.push(new Cell(i, j));
49+
}
50+
newMaze.push(row);
51+
}
52+
setMaze(newMaze);
53+
setStartPosition(flatHeight, flatWidth);
54+
setEndPosition(flatHeight, flatWidth);
55+
}
56+
57+
function clearAttribute(attribute: keyof Cell) {
58+
if (maze.length === 0 || maze[0].length === 0) {
59+
console.error(`Maze is empty or not initialized.`);
60+
return;
61+
}
62+
63+
const sampleCell = maze[0][0];
64+
const validAttributes = Object.keys(sampleCell);
65+
66+
if (!validAttributes.includes(attribute)) {
67+
console.error(`Invalid attribute: ${attribute}`);
68+
return;
69+
}
70+
71+
setMaze(prevMaze => prevMaze.map(row =>
72+
row.map(cell => ({ ...cell, [attribute]: false }))
73+
));
74+
};
75+
76+
async function populateOptimalPath(optimalPath: Cell[] | null) {
77+
if (!optimalPath) {
78+
return;
79+
}
80+
81+
const delay = 50; // Delay in milliseconds between each update
82+
83+
const updateCell = (index: number) => {
84+
if (index >= optimalPath.length) {
85+
return;
86+
}
87+
88+
setMaze(prevMaze => {
89+
const newMaze = prevMaze.map(row => row.map(cell => ({ ...cell })));
90+
newMaze[optimalPath[index].row][optimalPath[index].col].finalPath = true;
91+
return newMaze;
92+
});
93+
94+
// Schedule the next update with delay
95+
requestAnimationFrame(() => setTimeout(() => updateCell(index + 1), delay));
96+
};
97+
98+
updateCell(0);
99+
}
100+
101+
return {
102+
maze,
103+
setMaze,
104+
startNode,
105+
endNode,
106+
setStartNode,
107+
setEndNode,
108+
generateTable,
109+
clearAttribute,
110+
populateOptimalPath,
111+
};
112+
};
113+
114+
export default mazeHooks;

vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import react from '@vitejs/plugin-react'
33

44
// https://vitejs.dev/config/
55
export default defineConfig({
6-
base: "/",
6+
base: "/Algorithm-Visualiser/",
77
plugins: [react()],
88
})

0 commit comments

Comments
 (0)