import java.util.*;

public class Main {
    public static Map<String, MarsAction> calculateActions(
            GameState<MarsView> state, Identity identity) {
        var actions = new LinkedHashMap<String, MarsAction>();

        for (var worker : state.view().units()) {
            if (!worker.teamId().equals(identity.teamId())) continue;

            // Strategy goes here.

            actions.put(worker.unitId(), MarsAction.pass());
        }

        return actions;
    }

    // Game helpers. Feel free to use, discard, or modify.
    // Fill in the strategy section above.
    private static boolean inBounds(CoordinateGrid grid, Coordinate p) {
        return p.x() >= 0 && p.y() >= 0 && p.x() < grid.width() && p.y() < grid.height();
    }

    public static Set<String> isClaimed(List<Base> bases, Coordinate p) {
        var claims = new LinkedHashSet<String>();
        for (var base : bases) {
            int distance = Math.abs(base.position().x() - p.x()) + Math.abs(base.position().y() - p.y());
            if (distance <= 2) claims.add(base.teamId());
        }

        return claims;
    }

    public static String isBase(List<Base> bases, Coordinate p) {
        return bases.stream()
            .filter(base -> base.position().equals(p))
            .map(Base::teamId)
            .findFirst()
            .orElse(null);
    }

    public static String isOre(CoordinateGrid grid, Coordinate p) {
        if (!inBounds(grid, p)) return null;

        var type = grid.terrain(p);
        return (type.equals("gold") || type.equals("iron")) && grid.amount(p) > 0 ? type : null;
    }

    public static boolean canMoveOnto(CoordinateGrid grid, List<Base> bases, Coordinate p, String teamId) {
        if (!inBounds(grid, p)) return false;

        var type = grid.terrain(p);
        var owner = isBase(bases, p);
        return !type.equals("hidden") && !type.equals("void") && (owner == null || owner.equals(teamId));
    }

    /** Cost until ready at destination; start must be ready. Null means no known route. */
    public static List<Coordinate> pathTo(
            CoordinateGrid grid, Coordinate start, Coordinate end,
            List<Base> bases, String teamId, int obstacleCost) {
        if (obstacleCost < 1
                || !canMoveOnto(grid, bases, start, teamId)
                || !canMoveOnto(grid, bases, end, teamId)) {
            return null;
        }

        var distance = new HashMap<Coordinate, Long>();
        var previous = new HashMap<Coordinate, Coordinate>();
        record Step(Coordinate position, long cost) {}
        var queue = new PriorityQueue<Step>(Comparator.comparingLong(Step::cost));
        distance.put(start, 0L);
        queue.add(new Step(start, 0));

        while (!queue.isEmpty()) {
            var step = queue.remove();
            var p = step.position();
            if (step.cost() != distance.get(p)) continue;
            if (p.equals(end)) break;

            for (int dy = -1; dy <= 1; dy++) {
                for (int dx = -1; dx <= 1; dx++) {
                    if (dx == 0 && dy == 0) continue;
                    var next = new Coordinate(p.x() + dx, p.y() + dy);
                    if (!canMoveOnto(grid, bases, next, teamId)) continue;

                    var type = grid.terrain(next);
                    long cost = distance.get(p) + (type.equals("mountain") || type.equals("crater") ? obstacleCost : 1);
                    if (cost >= distance.getOrDefault(next, Long.MAX_VALUE)) continue;

                    distance.put(next, cost);
                    previous.put(next, p);
                    queue.add(new Step(next, cost));
                }
            }
        }

        if (!distance.containsKey(end)) return null;

        var path = new ArrayList<Coordinate>();
        for (var p = end; !p.equals(start); p = previous.get(p)) path.add(p);

        Collections.reverse(path);
        return path;
    }

    public static List<Coordinate> pathTo(CoordinateGrid grid, Coordinate start, Coordinate end) {
        return pathTo(grid, start, end, List.of(), null, 3);
    }

    public static void main(String[] args) throws Exception {
        try {
            new BotClient().runMars(Main::calculateActions);
        } catch (Exception error) {
            System.err.println(error.getMessage());
            System.exit(1);
        }
    }
}
