package com.galvarez.ttw.model.map; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.function.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PathFinding { private static final Logger log = LoggerFactory.getLogger(PathFinding.class); private final GameMap map; public PathFinding(GameMap map) { this.map = map; } private static int heuristic(MapPosition a, MapPosition b) { return MapTools.distance(a, b) * Terrain.FOREST.moveCost(); } private static final class Pos implements Comparable<Pos> { private final MapPosition p; private final int s; public Pos(MapPosition p, int s) { this.p = p; this.s = s; } @Override public int compareTo(Pos o) { return Integer.compare(s, o.s); } @Override public String toString() { return p.toString() + '=' + s; } } public List<MapPosition> aStarSearch(MapPosition start, MapPosition goal, Predicate<MapPosition> canMoveTo) { PriorityQueue<Pos> frontier = new PriorityQueue<Pos>(); frontier.add(new Pos(start, 0)); Map<MapPosition, MapPosition> cameFrom = new HashMap<>(); Map<MapPosition, Integer> costSoFar = new HashMap<>(); cameFrom.put(start, null); costSoFar.put(start, 0); while (!frontier.isEmpty()) { Pos current = frontier.poll(); if (current.p.equals(goal)) break; for (MapPosition next : neighbors(current.p, canMoveTo)) { int newCost = costSoFar.get(current.p) + cost(current.p, next); if (!costSoFar.containsKey(next) || newCost < costSoFar.get(next)) { costSoFar.put(next, newCost); frontier.add(new Pos(next, newCost + heuristic(goal, next))); cameFrom.put(next, current.p); } } } return path(cameFrom, start, goal); } private int cost(MapPosition p, MapPosition next) { // TODO should depend on influence cost for empire // TODO start is important: on water first step costs the most int cost = map.getTerrainAt(next).moveCost(); if (map.getEntityAt(p) != null) // will block if other entity do not move return cost * 2; else return cost; } private Iterable<MapPosition> neighbors(MapPosition p, Predicate<MapPosition> canMoveTo) { List<MapPosition> list = new ArrayList<>(); for (MapPosition n : map.getNeighbors(p)) if (canMoveTo.test(n)) list.add(n); return list; } private static List<MapPosition> path(Map<MapPosition, MapPosition> cameFrom, MapPosition start, MapPosition goal) { List<MapPosition> path = new ArrayList<>(); MapPosition current = goal; while (!current.equals(start)) { path.add(current); current = cameFrom.get(current); if (current == null) { log.debug("Cannot compute path from {} to {}.", start, goal); return null; } } Collections.reverse(path); return path; } }