Skip to content

Commit

Permalink
Search algo improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
sadanandpai committed Mar 11, 2024
1 parent c128e6c commit ce29288
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 109 deletions.
39 changes: 8 additions & 31 deletions src/apps/path-finder/algorithms/path-finder/bfs.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AlgoProps, Cell, CellType } from '../../models/interfaces';
import { delay } from '@/lib/helpers/async';
import { SearchAlgoProps, Cell, CellType } from '../../models/interfaces';
import { generateGrid } from '../../helpers/grid';

function getAddToQueueIfAllowedFunction(
Expand All @@ -26,14 +25,12 @@ function getAddToQueueIfAllowedFunction(

// The Breadth First Search Algorithm
export async function startBFSAlgo({
grid,
grid: stateGrid,
entry,
exit,
setCell,
setCells,
isRunning,
delayDuration,
}: AlgoProps) {
updateCells,
}: SearchAlgoProps) {
const grid = stateGrid.map((row) => row.slice());
const rows = grid.length;
const cols = grid[0].length;
const visited = generateGrid(rows, cols, false); // initalize visited arrays
Expand All @@ -50,7 +47,6 @@ export async function startBFSAlgo({
queue
);

const coveredCells = [];
while (queue.length) {
// iterate till queue items are over
const length = queue.length;
Expand All @@ -60,14 +56,8 @@ export async function startBFSAlgo({
const value = queue.shift()!;

if (value.row === exit.row && value.col === exit.col) {
setCells(coveredCells, CellType.fill);

// if exit is found, stop searching
return parents;
}

if (!isRunning()) {
return null;
return { grid, parents };
}

// Validate and add next coordinates to the queue (All 4 directions i.e up, down, left, right)
Expand All @@ -93,23 +83,10 @@ export async function startBFSAlgo({
}

if (grid[value.row][value.col] === CellType.clear) {
if (delayDuration > 0) {
setCell({ row: value.row, col: value.col }, CellType.fill);
} else {
coveredCells.push({ row: value.row, col: value.col });
}
await updateCells(grid, value, CellType.fill);
}
}

if (delayDuration > 0) {
await delay(delayDuration * 4);
}

if (!isRunning()) {
return null;
}
}

setCells(coveredCells, CellType.fill);
return null;
return { grid, parents: null };
}
54 changes: 16 additions & 38 deletions src/apps/path-finder/algorithms/path-finder/dfs.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,19 @@
import { AlgoProps, Cell, CellType } from '../../models/interfaces';
import { delay } from '@/lib/helpers/async';
import { SearchAlgoProps, Cell, CellType } from '../../models/interfaces';
import { generateGrid } from '../../helpers/grid';

// The Depth First Search Algorithm
export async function startDFSAlgo({
grid,
grid: stateGrid,
entry,
exit,
setCell,
setCells,
isRunning,
delayDuration,
}: AlgoProps) {
const rows = grid.length;
const cols = grid[0].length;
const visited = generateGrid(rows, cols, false); // initalize visited arrays
const parents = generateGrid<Cell>(rows, cols, null); // initalize parents arrays

updateCells,
}: SearchAlgoProps) {
async function explorePath(
row: number,
col: number,
coveredCells: Cell[],
parentRow = -1,
parentCol = -1
): Promise<boolean> {
if (!isRunning()) {
return false;
}

if (row < 0 || col < 0 || row >= rows || col >= cols) {
return false;
}
Expand All @@ -44,31 +30,23 @@ export async function startDFSAlgo({
}

if (parentCol !== -1 && parentRow !== -1) {
if (delayDuration > 0) {
setCell({ row, col }, CellType.fill);
} else {
coveredCells.push({ row: row, col: col });
}
}

if (delayDuration > 0) {
await delay(delayDuration);
await updateCells(grid, { row, col }, CellType.fill);
}

return (
(await explorePath(row + 1, col, coveredCells, row, col)) ||
(await explorePath(row - 1, col, coveredCells, row, col)) ||
(await explorePath(row, col + 1, coveredCells, row, col)) ||
(await explorePath(row, col - 1, coveredCells, row, col))
(await explorePath(row + 1, col, row, col)) ||
(await explorePath(row - 1, col, row, col)) ||
(await explorePath(row, col + 1, row, col)) ||
(await explorePath(row, col - 1, row, col))
);
}

const coveredCells: Cell[] = [];
const hasPath = await explorePath(entry.row, entry.col, coveredCells);

if (coveredCells.length) {
setCells(coveredCells, CellType.fill);
}
const grid = stateGrid.map((row) => row.slice());
const rows = grid.length;
const cols = grid[0].length;
const visited = generateGrid(rows, cols, false); // initalize visited arrays
const parents = generateGrid<Cell>(rows, cols, null); // initalize parents arrays
const hasPath = await explorePath(entry.row, entry.col);

return hasPath ? parents : null;
return hasPath ? { grid, parents } : { grid, parents: null };
}
2 changes: 1 addition & 1 deletion src/apps/path-finder/components/controller/execution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Status } from '../../models/interfaces';
import { searchPath } from '../../store/search-thunk';

const speeds = new Map([
['0.5x', 50],
['0.5x', 250],
['0.7x', 40],
['1x', 30],
['2x', 20],
Expand Down
11 changes: 6 additions & 5 deletions src/apps/path-finder/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ export interface AppState {
status: Status;
}

export interface AlgoProps {
export interface SearchAlgoProps {
grid: number[][];
entry: Cell;
exit: Cell;
setCell: (value: Cell, cellType: CellType) => void;
setCells: (value: Cell[], cellType: CellType) => void;
isRunning: () => boolean;
delayDuration: number;
updateCells: (
grid: number[][],
cells: Cell | Cell[],
cellType?: CellType
) => Promise<void>;
}

export interface MazeAlgoProps {
Expand Down
95 changes: 61 additions & 34 deletions src/apps/path-finder/store/search-thunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,83 @@ import { AppDispatch, RootState } from '@/host/store/store';
import {
setCells as setStateCells,
setCell as setStateCell,
setGrid,
setStatus,
} from './path-finder.slice';

import { AlgoProps, Cell, CellType, Status } from '../models/interfaces';
import { SearchAlgoProps, Cell, CellType, Status } from '../models/interfaces';
import { toast } from 'sonner';
import { tracePath } from '../helpers/path.helper';
import { delay } from '@/lib/helpers/async';

export function searchPath(
pathFinderAlgo: (props: AlgoProps) => Promise<Cell[][] | null>,
pathFinderAlgo: (
props: SearchAlgoProps
) => Promise<{ grid: CellType[][]; parents: Cell[][] | null }>,
delayDuration: number
) {
return async (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState().pathFinder;
dispatch(setStatus(Status.Searching));

const parents = await pathFinderAlgo({
grid: state.grid,
entry: state.entry,
exit: state.exit,
setCell: (value: Cell, cellType: CellType) =>
dispatch(setStateCell({ ...value, cellType })),
setCells: (cells: Cell[], cellType: CellType) => {
dispatch(setStateCells({ cells, cellType }));
},
isRunning: () => getState().pathFinder.status === Status.Searching,
delayDuration,
});

if (getState().pathFinder.status !== Status.Searching) {
return;
function isSearching() {
return getState().pathFinder.status === Status.Searching;
}

if (parents) {
toast.success('Path found!!! πŸ˜ƒ');

const pathLength = await tracePath(
parents,
state.entry!,
state.exit!,
(value: { row: number; col: number }) =>
dispatch(setStateCell({ ...value, cellType: CellType.path })),
() => getState().pathFinder.status === Status.Searching,
delayDuration * 2
);

toast('Path length is ' + (pathLength + 1));
} else {
toast.error('No path found πŸ˜”');
async function updateCells(
grid: CellType[][],
cells: Cell | Cell[],
cellType = CellType.clear
) {
if (!isSearching()) {
throw new Error('Path search cancelled');
}

if (!Array.isArray(cells)) {
cells = [cells];
}

cells.forEach((cell) => {
grid[cell.row][cell.col] = cellType;
});

if (delayDuration) {
dispatch(setStateCells({ cells, cellType }));
await delay(delayDuration);
}
}

dispatch(setStatus(Status.Complete));
try {
const { grid, parents } = await pathFinderAlgo({
grid: state.grid,
entry: state.entry,
exit: state.exit,
updateCells,
});

dispatch(setGrid({ grid }));

if (parents) {
toast.success('Path found!!! πŸ˜ƒ');

const pathLength = await tracePath(
parents,
state.entry!,
state.exit!,
(value: { row: number; col: number }) =>
dispatch(setStateCell({ ...value, cellType: CellType.path })),
() => getState().pathFinder.status === Status.Searching,
delayDuration * 2
);

toast('Path length is ' + (pathLength + 1));
} else {
toast.error('No path found πŸ˜”');
}

dispatch(setStatus(Status.Complete));
} catch {
// Do nothing
}
};
}

0 comments on commit ce29288

Please sign in to comment.