Skip to content

Commit aac3d15

Browse files
committed
Move Pathfinding algorithms to generator system to utilise requestAnimationFrame which significantly improves runtime performance
1 parent 6e783bb commit aac3d15

File tree

5 files changed

+70
-25
lines changed

5 files changed

+70
-25
lines changed

src/algorithms/mazes/DFSMazeGeneration.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Cell } from "../utils/PathfindingUtils";
2-
import { useMazeStore } from '../../hooks/useMazeStore';
32

43
function getNeighbors(maze: Cell[][], cell: Cell): Cell[] {
54
const neighbors: Cell[] = [];
@@ -30,9 +29,7 @@ function initialiseAllWalls(maze: Cell[][]): Cell[][] {
3029
);
3130
}
3231

33-
export async function dfsMazeGeneration(startCell: Cell, endCell: Cell) {
34-
const { maze, setMaze } = useMazeStore.getState();
35-
32+
export async function dfsMazeGeneration(maze: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void): Promise<void> {
3633
let newMaze = initialiseAllWalls(maze);
3734
let stack = [newMaze[1][1]];
3835
newMaze[1][1].wall = false;

src/algorithms/pathfinding/DepthFirstSearch.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { Cell, getOptimalPath } from '../utils/PathfindingUtils';
2-
import { sleep } from '../utils/SleepTime';
2+
import { useMazeStore } from '../../hooks/useMazeStore';
33

4-
export async function DepthFirstSearch(maze: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void ): Promise<Cell[] | null> {
4+
// Define a generator function for depth-first search on the maze
5+
function* dfsGenerator(maze: Cell[][], startCell: Cell, endCell: Cell): Generator<Cell[][] | Cell[] | null, unknown> {
56
const stack: Cell[] = [];
67
const visited: boolean[][] = [];
8+
79
for (let row = 0; row < maze.length; row++) {
810
visited[row] = [];
911
for (let col = 0; col < maze[row].length; col++) {
@@ -22,27 +24,51 @@ export async function DepthFirstSearch(maze: Cell[][], startCell: Cell, endCell:
2224
}
2325

2426
currentCell.visited = true;
25-
27+
2628
if (currentCell.row === endCell.row && currentCell.col === endCell.col) {
2729
return getOptimalPath(maze, startCell, endCell);
2830
}
2931

3032
let neighbors = getNeighbors(maze, currentCell);
3133

3234
for (let neighbor of neighbors) {
33-
if (neighbor.visited) {
34-
continue;
35+
if (!visited[neighbor.row][neighbor.col] && !neighbor.visited) {
36+
neighbor.prev = currentCell;
37+
stack.push(neighbor);
38+
visited[neighbor.row][neighbor.col] = true;
39+
maze[neighbor.row][neighbor.col].visited = true;
3540
}
36-
neighbor.prev = currentCell;
37-
stack.push(neighbor);
3841
}
39-
await sleep(10);
40-
setMaze([...maze]);
42+
yield maze;
4143
}
4244

43-
return null;
45+
yield null;
46+
}
47+
48+
// Define a function to animate the depth-first search
49+
export async function DepthFirstSearch(startCell: Cell, endCell: Cell): Promise<Cell[] | null> {
50+
const { maze, setMaze, populateOptimalPath } = useMazeStore.getState();
51+
const generator = dfsGenerator(maze, startCell, endCell);
52+
53+
return new Promise((resolve) => {
54+
function step() {
55+
const result = generator.next();
56+
if (result.done) {
57+
const optimalPath = result.value as Cell[] | null;
58+
if (optimalPath) {
59+
populateOptimalPath(optimalPath);
60+
}
61+
resolve(optimalPath);
62+
} else {
63+
setMaze(result.value as Cell[][]);
64+
requestAnimationFrame(step);
65+
}
66+
}
67+
requestAnimationFrame(step);
68+
});
4469
}
4570

71+
// Helper function to get the neighbors of a cell
4672
export function getNeighbors(cells: Cell[][], cell: Cell): Cell[] {
4773
const neighbors: Cell[] = [];
4874
const { row, col } = cell;

src/algorithms/pathfinding/Dijkstra.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Cell, getNeighbors} from '../utils/PathfindingUtils';
2-
import { sleep } from '../utils/SleepTime';
1+
import { Cell, getNeighbors } from '../utils/PathfindingUtils';
2+
import { useMazeStore } from '../../hooks/useMazeStore';
33

4-
export async function Dijkstra(cells: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void ): Promise<Cell[]> {
4+
function* dijkstraGenerator(cells: Cell[][], startCell: Cell, endCell: Cell): Generator<Cell[][] | Cell[], unknown> {
55
const queue: Cell[] = [];
66
startCell.visited = true;
7+
startCell.distance = 0;
78
queue.push(startCell);
89

910
while (queue.length > 0) {
@@ -24,11 +25,32 @@ export async function Dijkstra(cells: Cell[][], startCell: Cell, endCell: Cell,
2425
}
2526
}
2627
}
27-
await sleep(10);
28-
setMaze([...cells]);
28+
yield cells;
2929
}
3030

31-
return [];
31+
yield [];
32+
}
33+
34+
export async function Dijkstra(startCell: Cell, endCell: Cell): Promise<Cell[] | null>{
35+
const { maze, setMaze, populateOptimalPath } = useMazeStore.getState();
36+
const generator = dijkstraGenerator(maze, startCell, endCell);
37+
38+
return new Promise((resolve) => {
39+
function step() {
40+
const result = generator.next();
41+
if (result.done) {
42+
const optimalPath = result.value as Cell[] | null;
43+
if (optimalPath) {
44+
populateOptimalPath(optimalPath);
45+
}
46+
resolve(optimalPath);
47+
} else {
48+
setMaze(result.value as Cell[][]);
49+
requestAnimationFrame(step);
50+
}
51+
}
52+
requestAnimationFrame(step);
53+
});
3254
}
3355

3456
function getPath(endCell: Cell): Cell[] {
@@ -41,4 +63,4 @@ function getPath(endCell: Cell): Cell[] {
4163
}
4264

4365
return path;
44-
}
66+
}

src/hooks/useMazeStore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export const useMazeStore = create<MazeState>((set) => ({
118118
},
119119
moveNode: (row, col, nodeType) => {
120120
set((state) => {
121-
const newMaze = state.maze.map((r, i) =>
121+
const newMaze = state.maze.map((r) =>
122122
r.map((c) => {
123123
if (c.row === row && c.col === col) {
124124
return { ...c, [nodeType]: true };

src/utils/pathfindingAlgorithmHooks.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,18 @@ const pathfindingAlgorithmHooks = ({
4343
if (optimalPath) populateOptimalPath(optimalPath);
4444
},
4545
[Algorithm.DIJKSTRA]: async () => {
46-
const optimalPath = await Dijkstra(maze, startCell, endCell, setMaze);
46+
const optimalPath = await Dijkstra(startCell, endCell);
4747
if (optimalPath) populateOptimalPath(optimalPath);
4848
},
4949
[Algorithm.DFS]: async () => {
50-
const optimalPath = await DepthFirstSearch(maze, startCell, endCell, setMaze);
50+
const optimalPath = await DepthFirstSearch(startCell, endCell);
5151
if (optimalPath) populateOptimalPath(optimalPath);
5252
},
5353
[Algorithm.PRIMS_MAZE]: () => {
5454
primsMazeGeneration(maze, startCell, endCell, setMaze);
5555
},
5656
[Algorithm.DFS_MAZE]: () => {
57-
dfsMazeGeneration(startCell, endCell);
57+
dfsMazeGeneration(maze, startCell, endCell, setMaze);
5858
},
5959
[Algorithm.KRUSKAL_MAZE]: () => {
6060
kruskalsMazeGeneration(maze, startCell, endCell, setMaze);

0 commit comments

Comments
 (0)