Skip to content

Commit e8a7086

Browse files
committed
check grammar
1 parent 499dc31 commit e8a7086

File tree

1 file changed

+20
-20
lines changed
  • src/content/post/2025/07-31-maze-solver

1 file changed

+20
-20
lines changed

src/content/post/2025/07-31-maze-solver/index.mdx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ import YarnCoverageImage from '../../../../content/post/2025/07-31-maze-solver/_
2424

2525
## Introduction
2626

27-
Pathfinding is a fundamental topic in computer science, with applications in fields like navigation, AI/ML, network routing and many other. In this article, we compare four core pathfinding algorithms: breadth-first search (BFS), depth-first search (DFS), Dijkstra’s algorithm, and A* (A star) through a practical maze solving example. We don’t just explain them in theory, we built a demo app where you can tweak maze inputs or edit the algorithm code and instantly see how it affects the output and efficiency.
27+
Pathfinding is a fundamental topic in computer science, with applications in fields like navigation, AI/ML, network routing, and many others. In this article, we compare four core pathfinding algorithms: breadth-first search (BFS), depth-first search (DFS), Dijkstra’s algorithm, and A* (A star) through a practical maze-solving example. We don’t just explain them in theory, we built a demo app where you can tweak maze inputs or edit the algorithm code and instantly see how it affects the output and efficiency.
2828

2929
One of the key takeaways is how a tiny change, just a single line in the code can drastically alter an algorithm’s behavior. This highlights how critical implementation details are, even when the overall structure looks the same.
3030

3131
## Problem overview
3232

33-
Paths in a maze form a tree structure or a graph if a maze has cycles. Thats why tree and graph traversal algorithms can be used for finding paths and the shortest path in a maze.
33+
Paths in a maze form a tree structure or a graph if the maze contains cycles. That's why tree and graph traversal algorithms can be used for finding paths and the shortest path in a maze.
3434

35-
All 4 algorithms differ in just a few lines of code, but they behavior differs dramatically.
35+
All 4 algorithms differ in just a few lines of code, but their behavior differs dramatically.
3636

3737
## App architecture
3838

@@ -71,7 +71,7 @@ const end: Coordinate = { x: 4, y: 4 };
7171

7272
### Class structure
7373

74-
We use polymorphism and simplified Factory pattern. `MazeSolver` is an abstract class that declares `findPath()` method that is implemented in each derived concrete solver class.
74+
We use polymorphism and a simplified Factory pattern. `MazeSolver` is an abstract class that declares the `findPath()` method, which is implemented in each derived concrete solver class.
7575

7676
```ts title="src/solvers/maze-solver.ts"
7777
/**
@@ -132,7 +132,7 @@ const _maze2: IMaze = Maze.create(testMaze, start, end);
132132

133133
## Running the app
134134

135-
We install dependencies, run the app and run the tests as usual, like any other Typescript app.
135+
We install dependencies, run the app, and run the tests as usual, like any other TypeScript app.
136136

137137
```bash
138138
# install dependencies
@@ -155,7 +155,7 @@ yarn test-verbose
155155
yarn coverage
156156
```
157157

158-
We can see that different algorithms require different number of steps for the same input maze. Example output for a given maze input:
158+
We can see that different algorithms require a different number of steps for the same input maze. Example output for a given maze input:
159159

160160
<Image {...IMAGE_SIZES.FIXED.MDX_XS} src={YarnDevImage} alt="App dev mode terminal output" />
161161

@@ -173,13 +173,13 @@ And calculate the code coverage:
173173

174174
## Algorithms analysis and discussion
175175

176-
Now the most important and interesting part. Let's analyze the algorithms code and explain how it affects their behavior and efficiency.
176+
Now for the most important and interesting part: let's analyze the algorithm's code and explain how it affects their behavior and efficiency.
177177

178178
### Unweighted graphs
179179

180-
BFS and DFS are basic traversal algorithms that ignore the weights of the edges so they are applicable only on unweighted graphs.
180+
BFS and DFS are basic traversal algorithms that ignore the weights of the edges, so they are applicable only to unweighted graphs.
181181

182-
The actual code for BFS and DFS has only a single line difference, but they have a completely opposite behavior. BFS uses a queue (FIFO), and DFS uses a stack (LIFO) and this has a fundamental impact on the way the next node candidate for the path is selected.
182+
The actual code for BFS and DFS differs by only a single line, but they exhibit completely opposite behavior. BFS uses a queue (FIFO), while DFS uses a stack (LIFO), and this has a fundamental impact on how the next node candidate for the path is selected.
183183

184184
```ts
185185
// BFS
@@ -189,7 +189,7 @@ const { coord, path } = queue.shift()!;
189189
const { coord, path } = stack.pop()!;
190190
```
191191

192-
This is the array of coordinates that represents possible directions for a movement. This array is iterated over in the algorithms inner loop.
192+
This is the array of coordinates that represents possible directions for movement. This array is iterated over in the algorithm's inner loop.
193193

194194
```ts title="src/utils/constants.ts"
195195
export const directions: Direction[] = [
@@ -200,7 +200,7 @@ export const directions: Direction[] = [
200200
];
201201
```
202202

203-
Here is the complete BFS implementation (since all of the 4 algorithms share the most of the same base) so we can have a better idea what we are working with:
203+
Here is the complete BFS implementation (since all 4 algorithms share most of the same base) so we can have a better idea of what we are working with:
204204

205205
```ts title="src/solvers/maze-solver-bfs.ts"
206206
export class MazeSolverBFS extends MazeSolver {
@@ -263,19 +263,19 @@ export class MazeSolverBFS extends MazeSolver {
263263

264264
#### BFS
265265

266-
Since BFS uses a queue, it will respect this structure and attempt to change direction in every iteration of the outer loop. Without obstacles and boundaries, this will cause the algorithm to thoroughly inspect nodes closer to the starting node before moving further away. Thats why BFS can be inefficient for the large trees and graphs where the end node is very distant from the starting node.
266+
Since BFS uses a queue, it respects this structure and attempts to change direction in every iteration of the outer loop. Without obstacles and boundaries, this causes the algorithm to thoroughly inspect nodes closer to the starting node before moving further away. That's why BFS can be inefficient for large trees and graphs where the end node is very distant from the starting node.
267267

268268
#### DFS
269269

270-
In contrast, DFS will also respect initial order in the directions array but prioritizes the upper elements, so in the example above it will always attempt to apply up direction first before exploring other directions. Without obstacles and boundaries this will cause the algorithm to inspect the distant nodes in a straight line. DFS can be efficient to find distant end node efficiently but also can be very inefficient to find close node if it happens to be in the different direction.
270+
In contrast, DFS also respects the initial order in the directions array but prioritizes the earlier elements. So, in the example above, it will always attempt to apply the `up` direction first before exploring other directions. Without obstacles and boundaries, this causes the algorithm to inspect distant nodes in a straight line. DFS can be efficient for finding a distant end node but can also be very inefficient for finding a nearby node if it happens to be in a different direction.
271271

272272
### Weighted graphs
273273

274-
Not all graphs have edges with uniform weights. In such cases we must use algorithms that are aware of weights (cost between two nodes), Dijkstra and A* in this case.
274+
Not all graphs have edges with uniform weights. In such cases, we must use algorithms that are aware of weights (the cost between two nodes), such as Dijkstra and A*.
275275

276276
#### Dijkstra
277277

278-
Dijkstra is aware of the cost between two nodes (edge weight) and it will take it into account when selecting the next node. It uses a priority queue to keep the cost history.
278+
Dijkstra's algorithm is aware of the cost between two nodes (edge weight) and takes it into account when selecting the next node. It uses a priority queue to keep track of the cost history.
279279

280280
```ts title="src/solvers/maze-solver-dijkstra.ts"
281281
// Take the first element from the priority queue.
@@ -291,11 +291,11 @@ const nextCost = cost + this.maze.getCost(nextCoord);
291291
if( ... && !costMap.has(coordKey) || nextCost < costMap.get(coordKey)!)
292292
```
293293

294-
Dijkstra keeps a history of the cost of the current path and when selecting the next node it will choose the node that does minimal addition to the cost. If there are cycles it will access the same node from multiple paths and will choose the one with minimal weight (shortest path). In graphs with constant edge weights it reduces to BFS. That can be observed in the screenshot above where both BFS and Dijkstra take an equal number of steps, because the maze has uniform weight of 1 and Infinity.
294+
Dijkstra keeps a history of the cost of the current path and when selecting the next node chooses the node that adds the minimal cost. If there are cycles it may access the same node from multiple paths and will choose the one with the minimal weight (shortest path). In graphs with constant edge weights it reduces to BFS. This can be observed in the screenshot above, where both BFS and Dijkstra take an equal number of steps because the maze has uniform weights of 1 and Infinity.
295295

296296
#### A\*
297297

298-
A\* is same as Dijkstra but beside the keeping the history it uses the heuristics function to predict the future - the direction in which the end node could be.
298+
A\* is the same as Dijkstra but besides keeping the history, it uses a heuristic function to predict the future - the direction in which the end node could be.
299299

300300
```ts title="src/solvers/maze-solver-a-star.ts"
301301
protected heuristic(a: Coordinate, b: Coordinate): number {
@@ -324,11 +324,11 @@ If the heuristic function is well chosen it will make A\* more efficient than th
324324

325325
## Conclusion
326326

327-
On this example we could see how algorithm analysis and design is a very sensitive and subtle discipline, that leaves no room for a low focus or lack of understanding of the domain. Although BFS, DFS, Dijkstra, and A* share the majority of the implementation, just a subtle change in the code can cause a dramatic change in the behavior.
327+
In this example, we can see how algorithm analysis and design is a very sensitive and subtle discipline that leaves no room for low focus or a lack of understanding of the domain. Although BFS, DFS, Dijkstra, and A* share most of their implementation, even a subtle change in the code can lead to a dramatic change in behavior.
328328

329-
In the demo app you can tweak the predefined mazes in the `tests/fixtures/*.txt` files and try to make your own observations. You can also check the resources and the interactive playground listed in the [References](#references) section.
329+
In the demo app, you can tweak the predefined mazes in the `tests/fixtures/*.txt` files and make your own observations. You can also check the resources and interactive playground listed in the [References](#references) section.
330330

331-
Did you experiment with maze solving and pathfinding algorithms yourself before? Let me know in the comments.
331+
Have you experimented with maze-solving and pathfinding algorithms before? Let me know in the comments.
332332

333333
## References
334334

0 commit comments

Comments
 (0)