Skip to content

Commit 91d6595

Browse files
committed
Add in Maze Generation and additional pathfinding
* Pathfinding has become cumbersome, working on cleaning up and refactoring visualiser code
1 parent 3ffd5a8 commit 91d6595

File tree

8 files changed

+468
-46
lines changed

8 files changed

+468
-46
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Cell } from "../utils/PathfindingUtils";
2+
3+
function getNeighbors(maze: Cell[][], cell: Cell): Cell[] {
4+
const neighbors: Cell[] = [];
5+
const { row, col } = cell;
6+
7+
const directions = [
8+
{ row: -2, col: 0 }, // Up
9+
{ row: 2, col: 0 }, // Down
10+
{ row: 0, col: -2 }, // Left
11+
{ row: 0, col: 2 } // Right
12+
];
13+
14+
for (let direction of directions) {
15+
const newRow = row + direction.row;
16+
const newCol = col + direction.col;
17+
18+
if (newRow > 0 && newRow < maze.length - 1 && newCol > 0 && newCol < maze[0].length - 1) {
19+
neighbors.push(maze[newRow][newCol]);
20+
}
21+
}
22+
23+
return neighbors;
24+
}
25+
26+
function initialiseAllWalls(maze: Cell[][]): void {
27+
for (let row = 0; row < maze.length; row++) {
28+
for (let col = 0; col < maze[row].length; col++) {
29+
maze[row][col].wall = true;
30+
}
31+
}
32+
}
33+
34+
export function dfsMazeGeneration(maze: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void) {
35+
initialiseAllWalls(maze);
36+
37+
let stack = [maze[1][1]];
38+
maze[1][1].wall = false;
39+
40+
while (stack.length > 0) {
41+
let current = stack[stack.length - 1];
42+
let neighbors = getNeighbors(maze, current).filter(n => n.wall);
43+
44+
if (neighbors.length > 0) {
45+
let next = neighbors[Math.floor(Math.random() * neighbors.length)];
46+
stack.push(next);
47+
48+
// Remove the wall between the current cell and the chosen neighbor
49+
const midRow = current.row + (next.row - current.row) / 2;
50+
const midCol = current.col + (next.col - current.col) / 2;
51+
maze[midRow][midCol].wall = false;
52+
53+
// Mark the chosen neighbor as part of the maze
54+
next.wall = false;
55+
} else {
56+
stack.pop();
57+
}
58+
}
59+
60+
maze[startCell.row][startCell.col].wall = false;
61+
maze[endCell.row][endCell.col].wall = false;
62+
setMaze([...maze]);
63+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { Cell } from '../utils/PathfindingUtils';
2+
3+
type Maze = Cell[][];
4+
5+
function isInsideMaze(maze: Maze, row: number, col: number): boolean {
6+
return row > 0 && row < maze.length -1 && col > 0 && col < maze[0].length -1;
7+
}
8+
9+
function getWalls(maze: Maze, row: number, col: number): [number, number][] {
10+
const frontier: [number, number][] = [];
11+
if (isInsideMaze(maze, row - 2, col) && maze[row - 2][col].wall) {
12+
frontier.push([row - 2, col]);
13+
}
14+
if (isInsideMaze(maze, row + 2, col) && maze[row + 2][col].wall) {
15+
frontier.push([row + 2, col]);
16+
}
17+
if (isInsideMaze(maze, row, col - 2) && maze[row][col - 2].wall) {
18+
frontier.push([row, col - 2]);
19+
}
20+
if (isInsideMaze(maze, row, col + 2) && maze[row][col + 2].wall) {
21+
frontier.push([row, col + 2]);
22+
}
23+
return frontier;
24+
}
25+
26+
function getNeighbors(maze: Maze, row: number, col: number): [number, number][] {
27+
const neighbors: [number, number][] = [];
28+
if (isInsideMaze(maze, row - 2, col) && !maze[row - 2][col].wall) {
29+
neighbors.push([row - 2, col]);
30+
}
31+
if (isInsideMaze(maze, row + 2, col) && !maze[row + 2][col].wall) {
32+
neighbors.push([row + 2, col]);
33+
}
34+
if (isInsideMaze(maze, row, col - 2) && !maze[row][col - 2].wall) {
35+
neighbors.push([row, col - 2]);
36+
}
37+
if (isInsideMaze(maze, row, col + 2) && !maze[row][col + 2].wall) {
38+
neighbors.push([row, col + 2]);
39+
}
40+
return neighbors;
41+
}
42+
43+
function initialiseAllWalls(maze: Maze): void {
44+
for (let row = 0; row < maze.length; row++) {
45+
for (let col = 0; col < maze[row].length; col++) {
46+
maze[row][col].wall = true;
47+
}
48+
}
49+
}
50+
51+
function connectCells(maze: Maze, cell1: { row: number, col: number }, cell2: { row: number, col: number }): void {
52+
const betweenRow = Math.floor((cell1.row + cell2.row) / 2);
53+
const betweenCol = Math.floor((cell1.col + cell2.col) / 2);
54+
maze[betweenRow][betweenCol].wall = false;
55+
}
56+
57+
export function primsMazeGeneration(maze: Maze, startCell: Cell, endCell: Cell, setMaze: (arr: Maze) => void): void {
58+
if (maze.length < 3 || maze[0].length < 3) {
59+
return;
60+
}
61+
initialiseAllWalls(maze);
62+
maze[1][1].wall = false;
63+
64+
let frontier = getWalls(maze, 1,1);
65+
66+
while (frontier.length > 0) {
67+
const randomIndex = Math.floor(Math.random() * frontier.length);
68+
const [row, col] = frontier[randomIndex];
69+
frontier.splice(randomIndex, 1);
70+
71+
const neighbors = getNeighbors(maze, row, col);
72+
if (neighbors.length > 0) {
73+
const [neighborRow, neighborCol] = neighbors[Math.floor(Math.random() * neighbors.length)];
74+
maze[row][col].wall = false;
75+
connectCells(maze, { row, col }, { row: neighborRow, col: neighborCol });
76+
77+
const newFrontier = getWalls(maze, row, col);
78+
for (const cell of newFrontier) {
79+
if (!frontier.some(([fr, fc]) => fr === cell[0] && fc === cell[1])) {
80+
frontier.push(cell);
81+
}
82+
}
83+
}
84+
}
85+
maze[startCell.row][startCell.col].wall = false;
86+
maze[endCell.row][endCell.col].wall = false;
87+
setMaze([...maze]);
88+
}
Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
import { Cell } from "../utils/PathfindingUtils";
1+
import { Cell, getNeighbors, getOptimalPath } from "../utils/PathfindingUtils";
2+
import { sleep } from '../utils/SleepTime';
23

34
// Define a function to perform breadth-first search on the maze
4-
export function BreadthFirstSearch(maze: Cell[][], startCell: Cell, endCell: Cell): boolean[][] | null {
5+
export async function BreadthFirstSearch(maze: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void ): Promise<Cell[] | null> {
56
const queue: Cell[] = [];
67
const visited: boolean[][] = [];
78

89
// Initialize the visited array
910
for (let row = 0; row < maze.length; row++) {
1011
visited[row] = [];
1112
for (let col = 0; col < maze[row].length; col++) {
12-
visited[row][col] = true;
13+
visited[row][col] = false;
1314
}
1415
}
1516

1617
// Add the start cell to the queue
1718
queue.push(startCell);
1819
visited[startCell.row][startCell.col] = true;
20+
maze[startCell.row][startCell.col].visited = true;
1921

2022
// Perform breadth-first search
2123
while (queue.length > 0) {
2224
const currentCell = queue.shift();
23-
2425
if (!currentCell) {
2526
continue;
2627
}
27-
2828
// Check if we have reached the end cell
2929
if (currentCell.row === endCell.row && currentCell.col === endCell.col) {
30-
return visited;
30+
return getOptimalPath(maze, startCell, endCell);
3131
}
3232

3333
// Get the neighbors of the current cell
@@ -38,38 +38,14 @@ export function BreadthFirstSearch(maze: Cell[][], startCell: Cell, endCell: Cel
3838
if (!visited[neighbor.row][neighbor.col]) {
3939
queue.push(neighbor);
4040
visited[neighbor.row][neighbor.col] = true;
41+
maze[neighbor.row][neighbor.col].visited = true;
42+
maze[neighbor.row][neighbor.col].prev = currentCell;
4143
}
4244
}
45+
await sleep(10);
46+
setMaze([...maze]);
4347
}
4448

4549
// If we reach here, there is no path from start to end
4650
return null;
47-
}
48-
49-
// Define a function to get the neighbors of a cell
50-
function getNeighbors(maze: Cell[][], cell: Cell): Cell[] {
51-
const neighbors: Cell[] = [];
52-
const { row, col } = cell;
53-
54-
// Check the top neighbor
55-
if (row > 0 && !maze[row - 1][col].wall) {
56-
neighbors.push(maze[row - 1][col]);
57-
}
58-
59-
// Check the right neighbor
60-
if (col < maze[row].length - 1 && !maze[row][col + 1].wall) {
61-
neighbors.push(maze[row][col + 1]);
62-
}
63-
64-
// Check the bottom neighbor
65-
if (row < maze.length - 1 && !maze[row + 1][col].wall) {
66-
neighbors.push(maze[row + 1][col]);
67-
}
68-
69-
// Check the left neighbor
70-
if (col > 0 && !maze[row][col - 1].wall) {
71-
neighbors.push(maze[row][col - 1]);
72-
}
73-
74-
return neighbors;
7551
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Cell, getOptimalPath } from '../utils/PathfindingUtils';
2+
import { sleep } from '../utils/SleepTime';
3+
4+
export async function DepthFirstSearch(maze: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void ): Promise<Cell[] | null> {
5+
const stack: Cell[] = [];
6+
const visited: boolean[][] = [];
7+
for (let row = 0; row < maze.length; row++) {
8+
visited[row] = [];
9+
for (let col = 0; col < maze[row].length; col++) {
10+
visited[row][col] = false;
11+
}
12+
}
13+
14+
stack.push(startCell);
15+
visited[startCell.row][startCell.col] = true;
16+
maze[startCell.row][startCell.col].visited = true;
17+
18+
while (stack.length > 0) {
19+
const currentCell = stack.pop();
20+
if (!currentCell) {
21+
continue;
22+
}
23+
24+
currentCell.visited = true;
25+
26+
if (currentCell.row === endCell.row && currentCell.col === endCell.col) {
27+
return getOptimalPath(maze, startCell, endCell);
28+
}
29+
30+
let neighbors = getNeighbors(maze, currentCell);
31+
32+
for (let neighbor of neighbors) {
33+
if (neighbor.visited) {
34+
continue;
35+
}
36+
neighbor.prev = currentCell;
37+
stack.push(neighbor);
38+
}
39+
await sleep(10);
40+
setMaze([...maze]);
41+
}
42+
43+
return null;
44+
}
45+
46+
export function getNeighbors(cells: Cell[][], cell: Cell): Cell[] {
47+
const neighbors: Cell[] = [];
48+
const { row, col } = cell;
49+
50+
if (row > 0 && !cells[row - 1][col].wall) {
51+
neighbors.push(cells[row - 1][col]);
52+
}
53+
if (col > 0 && !cells[row][col - 1].wall) {
54+
neighbors.push(cells[row][col - 1]);
55+
}
56+
if (row < cells.length - 1 && !cells[row + 1][col].wall) {
57+
neighbors.push(cells[row + 1][col]);
58+
}
59+
if (col < cells[0].length - 1 && !cells[row][col + 1].wall) {
60+
neighbors.push(cells[row][col + 1]);
61+
}
62+
63+
return neighbors;
64+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Cell, getNeighbors} from '../utils/PathfindingUtils';
2+
import { sleep } from '../utils/SleepTime';
3+
4+
export async function Dijkstra(cells: Cell[][], startCell: Cell, endCell: Cell, setMaze: (arr: Cell[][]) => void ): Promise<Cell[]> {
5+
const queue: Cell[] = [];
6+
startCell.visited = true;
7+
queue.push(startCell);
8+
9+
while (queue.length > 0) {
10+
const currentCell = queue.shift()!;
11+
if (currentCell === endCell) {
12+
return getPath(endCell);
13+
}
14+
15+
const neighbors = getNeighbors(cells, currentCell);
16+
for (const neighbor of neighbors) {
17+
const newDistance = currentCell.distance + 1;
18+
if (!neighbor.visited || newDistance < neighbor.distance) {
19+
neighbor.distance = newDistance;
20+
neighbor.prev = currentCell;
21+
if (!neighbor.visited) {
22+
neighbor.visited = true;
23+
queue.push(neighbor);
24+
}
25+
}
26+
}
27+
await sleep(10);
28+
setMaze([...cells]);
29+
}
30+
31+
return [];
32+
}
33+
34+
function getPath(endCell: Cell): Cell[] {
35+
const path: Cell[] = [];
36+
let currentCell: Cell | null = endCell;
37+
38+
while (currentCell !== null) {
39+
path.unshift(currentCell);
40+
currentCell = currentCell.prev;
41+
}
42+
43+
return path;
44+
}

0 commit comments

Comments
 (0)