The scenario is a turn-based situation where the player has to move towards a location B from source location A, but can only move a maximum amount of units.
For example, B is 24 units away from A (calculated using BFS), and I’ve rolled a score of 12. How can I find the best path towards B that’s only 12 movement-units away?
Notes:
- Can not move diagonally
- There are large obstacles
Edit: This is for a game similar to Clue / Cluedo, but is text-only so the player will choose a direction to move ‘towards’.
Here is what I tried:
Example grid: (◘ is obstacle, ○ is not)
○○○○○○○○○○
○○○○○○○○◘◘
○○○◘◘◘○○◘◘
○○○◘◘◘○○B◘
○A○◘◘◘○○◘◘
Algorithm:
if paces == 0, return
try moving col closer to dest.col:
if col == dest.col, move row closer to dest.row
else if adjacent is blocked, move row away from start
This works okay on paper, except for when I run myself into a corner:
○A→◘◘○○◘◘◘
○○↓◘◘○○B◘◘
○○↓◘◘○○◘◘◘
○○↓◘◘○○↑◘◘
○○↓→→→→→◘◘
Solution:
public ArrayList<Location> shortestPath(final Location start, final Location dest) {
HashSet<Location> visits = new HashSet<>();
HashMap<Location, Location> links = new HashMap<>();
PriorityQueue<Location> queue = new PriorityQueue<>(Board.GRID_COLS * Board.GRID_ROWS,
new Comparator<Location>() {
@Override
public int compare(Location a, Location b) {
return Integer.compare(getHeuristic(a, dest), getHeuristic(b, dest));
}
});
queue.add(start);
while (!queue.isEmpty()) {
Location current = queue.remove();
if (current.equals(dest)) {
ArrayList<Location> path = reconstruct(current, new LinkedList<Location>(), links);
path.add(dest);
return path;
}
visits.add(current);
for (Location neighbour : getNeighbours(current)) {
if (!visits.contains(neighbour)) {
queue.add(neighbour);
visits.add(neighbour);
links.put(neighbour, current);
}
}
}
return null; // failed
}
// Manhattan distance
private int getHeuristic(Location src, Location dest) {
return Math.abs(dest.row - src.row) + Math.abs(dest.col - src.col);
}
private ArrayList<Location> reconstruct(Location current, LinkedList<Location> list, HashMap<Location, Location> links) {
if (links.containsKey(current)) {
list.addFirst(links.get(current));
return reconstruct(links.get(current), list, links);
} else {
return new ArrayList<>(list);
}
}
private ArrayList<Location> getNeighbours(Location current) {
ArrayList<Location> neighbours = new ArrayList<>();
if (current.row < GRID_ROWS - 1) {
Location n = LOCATIONS[current.row + 1][current.col];
if (isAccessible(n, current)) neighbours.add(n);
}
if (current.row > 0) {
Location n = LOCATIONS[current.row - 1][current.col];
if (isAccessible(n, current)) neighbours.add(n);
}
if (current.col < GRID_COLS - 1) {
Location n = LOCATIONS[current.row][current.col + 1];
if (isAccessible(n, current)) neighbours.add(n);
}
if (current.col > 0) {
Location n = LOCATIONS[current.row][current.col - 1];
if (isAccessible(n, current)) neighbours.add(n);
}
return neighbours;
}
Sounds like a perfect job for A*.
On your graph, it will basically just be the same (algorithmically) as breadth-first search, but using a priority-queue (ordered by
f(x)) rather than a queue.