Skip to content
← All writing
5 min read

Three pathfinders walk into a tower defense game

A*, Greedy Best-First Search and Dijkstra look similar on paper. In a game where the map keeps changing, they behave very differently.

Dynamic Tower Defense started as a way to make the algorithms from my AI course visible. The idea: put enemies on a grid, let the player build towers, and recompute the route whenever the board changes. Three algorithms share the same map and the same rules; the only difference is which node they open next.

The only real difference

All three maintain a frontier of nodes to explore. A* orders that frontier by g(n) + h(n) — the cost so far plus an estimate of the remaining cost. Greedy Best-First Search keeps only h(n). Dijkstra keeps only g(n).

A*       f = g + h
Greedy   f = h
Dijkstra f = g

With an admissible heuristic, A* finds a shortest path without exploring everything. Greedy is usually the fastest to a decision and the easiest to fool: a wall can make a node look close to the goal when the actual route is long. Dijkstra is the safe, expensive option — it will find the shortest path, but only after looking at a lot of the map.

Why the game is a better demo than a chart

On a static grid, A* and Dijkstra often end up visiting a similar set of cells, and the difference feels academic. Add a tower that changes the cost of a cell, or an enemy that re-routes while another one is still walking, and the trade-offs become visible. Greedy cuts corners and gets trapped. A* adjusts without flooding the board. Dijkstra keeps its promise and pays for it in exploration.

The debug overlay matters as much as the game. Drawing open and closed nodes in different colors turns the algorithm from a black box into something you can watch. The path is the output, but the search is the interesting part.

What I took away

  • A* is the default when you have a usable heuristic and care about the shortest path.
  • Greedy is a good first pass when speed matters more than optimality, but it needs a fallback.
  • Dijkstra is the reference implementation — useful for checking the other two, less useful when the map is large.
  • The same algorithm can feel completely different once the graph changes underneath it.

The project is a game, but the useful artifact is the comparison. It is easier to remember why a heuristic can be wrong after you have watched an enemy walk into a dead end.