Skip to content

Commit

Permalink
Merge pull request #1 from mkouhia/hero-dijkstra
Browse files Browse the repository at this point in the history
Implement hero movement
  • Loading branch information
mkouhia authored May 27, 2024
2 parents 992e69b + db40f54 commit 75efeb7
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 189 deletions.
39 changes: 0 additions & 39 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ repository = "https://github.com/mkouhia/wundernut-vol13"
anyhow = "1.0.86"
clap = { version = "4.5.4", features = ["derive"] }
itertools = "0.13.0"
petgraph = "0.6.5"
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,37 @@ See program help in

## Implementation

### Hero movement

As per the problem rules, the hero is required to take the _shortest path_ to the exit, while avoiding the dragon.
This problem statement implies, that we need to find the optimal solution.
This problem setup can be rephrased as finding the shortest paths between nodes in a [graph](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)), and one well-known algorithm to solve this proble is the [Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm). In our case, there are some excemptions and additions:
- The hero and the dragon can take steps in any cardinal direction, and the step distance from one tile to another is always one. Thus, the graph in _undirected_ and all _edge weights_ are equal to one.
- The dragon is not allowed to catch the hero. Thus, we need additional checks in the algorithm to weed out those branches.

In the implemented algorithm, the tiled map is first structured as graph, where each accessible tile is represented as a node. Each possible hero movement is recorded as `State`, which contains the hero position, dragon position and the number of steps taken until that step.

The search for the optimal path starts at the hero location.
1. Structs representing the steps to the neighbouring nodes are placed in a binary heap, where the structs are ordered by increasing cost.
2. The lowest cost struct is removed from the heap for evaluation. This way, we are always addressing the shortest path up until now.
3. We check if the hero position in the current state is the goal. If it is, we just found the shortest path to the goal.
4. For every neighbour of the hero node in current state, we evaluate possible hero movements, and after hero movement, where the dragon would go.
5. If the dragon would meet the hero, the state is not accepted.
6. All acceptable resulting states are pushed to the heap for evaluation.
7. We loop over the possible states, taking more steps one by one until the goal is found.


### Dragon movement

The dragon always wants to go towards the hero, taking the shortest possible path.
Once the maze has been set up, the shortest paths from one square to another does not change.
Thus, we can pre-calculate the shortest paths from any square `u` to any other square `v` with [Floyd-Warshall algorithm](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm#Path_reconstruction).
Upon calculating the shortest paths, the previous steps on the path `u->v`are saved, and queried upon dragon movement.

### General implementation notices
- If the maze would have a square type with single access direction, that could be handled with directional edges.
- Different terrain types could be handled by introducing edge weights.
- The solution is not optimized for memory usage. In the hero shortest path algorithm, one would typically check if the hero has already visited the node with a lower cost. However, as the dragon position matters, `dist` should include hero _and_ dragon position, and it now contains only hero positions as usize index. For larger mazes, more complex `dist` implementation could reduce binary heap size.

## Development

Expand Down
Loading

0 comments on commit 75efeb7

Please sign in to comment.