Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 52 additions & 172 deletions src/algorithms/pathfinding/dfs.js
Original file line number Diff line number Diff line change
@@ -1,184 +1,64 @@
export function dfs(grid, start, finish) {
const visitedInOrder = [];
let unvisited = [];
unvisited.push(start);
while (unvisited.length) {
const node = unvisited.pop();
if (node === finish) {
return visitedInOrder;
}
if (node.isWall) continue;
node.isVisited = true;
visitedInOrder.push(node);

unvisited = unvisited.concat(getUNeighbors(node, grid));
}

return visitedInOrder;
}
function updateUnvisitedNeighborsStar(cur, grid, finish) {
const neighbors = [];
const { row, col } = cur;
if (row > 0) neighbors.push(grid[row - 1][col]);
if (row < grid.length - 1) neighbors.push(grid[row + 1][col]);
if (col > 0) neighbors.push(grid[row][col - 1]);
if (col < grid[0].length - 1) neighbors.push(grid[row][col + 1]);
for (const neighbor of neighbors) {
if (!neighbor.isVisited) {
neighbor.distance = cur.distance + 1;
neighbor.heuristic = manhattanDistance(neighbor, finish);
neighbor.previousNode = cur;
}
}
}

function manhattanDistance(a, b) {
let { row: ar, col: ac } = a;
let { row: br, col: bc } = b;
return Math.abs(ar - br) + Math.abs(ac - bc);
}

function allNodes(grid) {
const re = [];
for (const row of grid) {
for (const node of row) {
re.push(node);
}
}
return re;
export function dfs(grid, startNode, finishNode) {
const visitedNodesInOrder = [];
startNode.distance = 0;

const unvisitedNodes = getAllNodes(grid);
while (!!unvisitedNodes.length) {
sortNodesByDistance(unvisitedNodes);
const closestNode = unvisitedNodes.shift();
// If we encounter a wall, we skip it.
if (closestNode.isWall) continue;
// If the closest node is at a distance of infinity,
// we must be trapped and should therefore stop.
if (closestNode.distance === Infinity) return visitedNodesInOrder;
closestNode.isVisited = true;
visitedNodesInOrder.push(closestNode);
if (closestNode === finishNode) return visitedNodesInOrder;
updateUnvisitedNeighbors(closestNode, grid);
}
}

function sortNodesStar(nodes) {
nodes.sort((a, b) => (a.distance + a.heuristic) - (b.distance + b.heuristic));
function sortNodesByDistance(unvisitedNodes) {
unvisitedNodes.sort(
(nodeA, nodeB) =>
nodeA.distance + nodeA.cost - (nodeB.distance + nodeB.cost)
);
}


function sortNodes(nodes) {
nodes.sort((a, b) => a.distance - b.distance);
function updateUnvisitedNeighbors(node, grid) {
const unvisitedNeighbors = getUnvisitedNeighbors(node, grid);
for (const neighbor of unvisitedNeighbors) {
neighbor.distance = node.distance + 1;
neighbor.previousNode = node;
}
}

function updateUnvisitedNeighbors(closest, grid) {
const neighbors = [];
const { row, col } = closest;
if (row > 0) neighbors.push(grid[row - 1][col]);
if (row < grid.length - 1) neighbors.push(grid[row + 1][col]);
if (col > 0) neighbors.push(grid[row][col - 1]);
if (col < grid[0].length - 1) neighbors.push(grid[row][col + 1]);
for (const neighbor of neighbors) {
if (!neighbor.isVisited) {
neighbor.distance = closest.distance + 1;
neighbor.previousNode = closest;
}
}
function getUnvisitedNeighbors(node, grid) {
const neighbors = [];
const { col, row } = node;
if (row > 0) neighbors.push(grid[row - 1][col]);
if (row < grid.length - 1) neighbors.push(grid[row + 1][col]);
if (col > 0) neighbors.push(grid[row][col - 1]);
if (col < grid[0].length - 1) neighbors.push(grid[row][col + 1]);
return neighbors.filter((neighbor) => !neighbor.isVisited);
}

function getShortestPath(finish) {
const path = [];
let cur = finish;
while (cur !== null) {
path.unshift(cur);
cur = cur.previousNode;
function getAllNodes(grid) {
const nodes = [];
for (const row of grid) {
for (const node of row) {
nodes.push(node);
}
return path;
}
return nodes;
}

function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
export function getNodesInShortestPathOrder(finishNode) {
const nodesInShortestPathOrder = [];
let currentNode = finishNode;
while (currentNode !== null) {
nodesInShortestPathOrder.unshift(currentNode);
currentNode = currentNode.previousNode;
}
return nodesInShortestPathOrder;
}

function primMaze(grid) {
let sr = 7, sc = 17; // set a starting point for generating maze
let height = grid.length, width = grid[0].length;
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
makeWall(grid, i, j, false);
}

}
for (let i = 0; i < height; i++) {
for (let j = i % 2 + 1; j < width; j += i % 2 + 1) {
makeWall(grid, i, j, true);
}
}
for (let i = 0; i < height; i++) {
makeWall(grid, i, 0, true);
}
let visited = [];
let path = [{ row: sr, col: sc }];
while (path.length > 0) {
const index = randomSelect(path);
const node = path[index];
path.splice(index, 1);
visited = visited.concat([node]);
const { c: connected, u: unconnected } = getNeighbors(grid, visited, node);
if (connected.length > 0) {
let rn = randomSelect(connected);
connect(grid, node, connected[rn]);
connected.splice(rn);
}
path = path.concat(unconnected);

}
}

function randomSelect(path) {
return randomInt(0, path.length - 1);
}

function validate(grid, points) {
let height = grid.length, width = grid[0].length;
let pRe = [];
for (let index = 0; index < points.length; index++) {
let { row, col } = points[index];
if ((0 <= row && row < height && 0 <= col && col < width)) {
pRe.push(points[index]);
}
}
return pRe;

}

function isVisited(visited, node) {
let { row: nr, col: nc } = node;
for (let index = 0; index < visited.length; index++) {
let { row: ir, col: ic } = visited[index];
if (nr === ir && nc === ic) {
return true;
}
}
return false;
}

function getNeighbors(grid, visited, node) {
let { row, col } = node;
let neighbors = [{ row: row + 2, col: col }, { row: row - 2, col: col }, { row: row, col: col + 2 }, { row: row, col: col - 2 }];
neighbors = validate(grid, neighbors.slice());
let connected = [];
let unconnected = [];
neighbors.forEach(neighbor => {
if (isVisited(visited, neighbor)) {
connected.push(neighbor);
}
else {
unconnected.push(neighbor);
}
});
return { c: connected, u: unconnected };
}

function connect(grid, a, b) {
let { row: ar, col: ac } = a;
let { row: br, col: bc } = b;
let row = (ar + br) / 2;
let col = (ac + bc) / 2;
makeWall(grid, row, col, false);
}

function makeWall(grid, row, col, isW) {
const node = grid[row][col];
const newNode = {
...node,
isWall: isW,
}
grid[row][col] = newNode;
}
1 change: 1 addition & 0 deletions src/components/Header/PathHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const Pathheader = (props) => {
>
<MenuItem value="dijkstra">Dijkstra</MenuItem>
<MenuItem value="a*">A*</MenuItem>
<MenuItem value="dfs">DFS</MenuItem>
</Select>
<Link className="flex" to="/">
<p style={{ marginRight: 10 }}>Sorting Algorithms</p> {arrowForward}
Expand Down
18 changes: 18 additions & 0 deletions src/components/PathfindingVisualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button } from "@mui/material";
import buttonStyles from "./ButtonStyle";
import { connect } from "react-redux";
import * as dijkstra from "../algorithms/pathfinding/dijkstra";
import * as dfs from "../algorithms/pathfinding/dfs";
import * as astar from "../algorithms/pathfinding/astar";

import Header from "../components/Header/Header";
Expand Down Expand Up @@ -154,6 +155,20 @@ class PathfindingVisualizer extends Component {
);
}

// Call this function on button click
visualizeDfs() {
const { grid } = this.state;
const startNode = grid[START_NODE_ROW][START_NODE_COL];
const finishNode = grid[FINISH_NODE_ROW][FINISH_NODE_COL];
const visitedNodesInOrder = dfs.dfs(grid, startNode, finishNode);
const nodesInShortestPathOrder =
dfs.getNodesInShortestPathOrder(finishNode);
this.animatePathfindingAlgorithm(
visitedNodesInOrder,
nodesInShortestPathOrder
);
}

// Call this function on button click
visualizeAstar() {
const { grid } = this.state;
Expand All @@ -177,6 +192,9 @@ class PathfindingVisualizer extends Component {
case "dijkstra":
this.visualizeDijkstra(this);
break;
case "dfs":
this.visualizeDfs(this);
break;
case "a*":
this.visualizeAstar(this);
break;
Expand Down
16 changes: 15 additions & 1 deletion src/pathfindingTutorial.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,21 @@ const pathData = [
title: "A* Search",
list: [
"A* does exactly the same as Dijkstra’s algorithm, visiting each node and adding it to a priority queue, the only modification is that the node at the top of the priority queue is the node which has the shortest distance cost + the cost to the final node from that node.",
"In this way, the nodes the nodes that are far away from the target are penalised.",
"In this way, the nodes that are far away from the target are penalised.",
],
},
{
title: "DFS Algorithm",
list: [
"Depth-First Search (DFS) is not typically used for finding the shortest path in a graph because it does not guarantee that it will find the shortest path.",
"It explores a graph by starting at an initial node and traversing as deeply as possible along each branch before backtracking.",
],
},
{
title: "DFS Algorithm",
list: [
"It marks the current node as visited, appends it to the path, and checks if it's the target node.",
"If not, it recursively explores the neighbors of the current node.",
],
},
];
Expand Down