Advertisement
Guest User

Untitled

a guest
Dec 27th, 2018
276
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.10 KB | None | 0 0
  1. package Advent2018;
  2.  
  3. import util.AdventOfCode;
  4. import util.Direction;
  5. import util.Node;
  6.  
  7. import java.util.*;
  8. import java.util.concurrent.atomic.AtomicInteger;
  9. import java.util.stream.Collectors;
  10. import static java.util.Comparator.*;
  11.  
  12. public class Day15 extends AdventOfCode {
  13.  
  14.     private int[][] grid;           // ASCII grid of map for display/pathfinding
  15.     private Unit[][] unitMap;       // lookup map for Elf/Goblin units
  16.     private List<Unit> units;       // List of units
  17.     private int rounds;             // current round number
  18.  
  19.     class Unit {
  20.         Node position;
  21.         boolean elf;            // true = Elf; false = Goblin
  22.         boolean alive = true;
  23.         int hitPoints = 200;
  24.         int attackPower = 3;
  25.  
  26.         int getX() { return position.getX(); }
  27.         int getY() { return position.getY(); }
  28.         int getHitPoints() { return hitPoints; }
  29.         boolean isAlive() { return alive; }
  30.         boolean isElf() { return elf; }
  31.  
  32.         boolean turn() {
  33.             if (alive) {
  34.                 List<Unit> enemies = getEnemies(this);
  35.                 if (enemies.size() == 0) return true;
  36.                 Unit enemy = getAdjacentEnemy(this);
  37.                 if (enemy == null) {
  38.                     move(enemies);
  39.                     enemy = getAdjacentEnemy(this);
  40.                     if (enemy == null) return false;
  41.                 }
  42.                 attack(enemy);
  43.             }
  44.             return false;
  45.         }
  46.  
  47.         void move(List<Unit> enemies) {
  48.             Node m = bestMove(this, enemies);
  49.             if (m != null) {
  50.                 grid[getX()][getY()] = '.';
  51.                 unitMap[getX()][getY()] = null;
  52.                 this.position = m;
  53.                 grid[getX()][getY()] = this.elf ? 'E' : 'G';
  54.                 unitMap[getX()][getY()] = this;
  55.             }
  56.         }
  57.  
  58.         void attack(Unit enemy) {
  59.             enemy.hitPoints -= this.attackPower;
  60.             if (enemy.hitPoints <= 0) {
  61.                 enemy.alive = false;
  62.                 unitMap[enemy.getX()][enemy.getY()] = null;
  63.                 grid[enemy.getX()][enemy.getY()] = '.';
  64.             }
  65.         }
  66.     }
  67.  
  68.     public Day15(List<String> input) {
  69.         super(input);
  70.         title = "Beverage Bandits";
  71.         part1Description = "Score after rounds complete: ";
  72.         part2Description = "Score for first elf win: ";
  73.     }
  74.  
  75.     // return a list of all enemies of unit n regardless of their position
  76.     private List<Unit> getEnemies(Unit n) {
  77.         return units.stream()
  78.                 .filter(x -> x.elf != n.elf)
  79.                 .filter(Unit::isAlive)
  80.                 .collect(Collectors.toList());
  81.     }
  82.  
  83.     // return the position Node for the best move for unit u
  84.     private Node bestMove(Unit u, List<Unit> enemies) {
  85.         List<List<Node>> paths = new ArrayList<>();
  86.  
  87.         // find all paths to valid adjacent to enemy squares
  88.         for (Unit enemy : enemies) {
  89.             for (Node each : getAdjacent(enemy.position)) {
  90.                 List<Node> path = bfs(u.position, each);
  91.                 if (!path.isEmpty()) paths.add(path);
  92.             }
  93.         }
  94.  
  95.         if (paths.size() == 0) return null;
  96.  
  97.         // find minimum length of paths
  98.         int min = paths.stream()
  99.                 .mapToInt(List::size)
  100.                 .min().orElse(0);
  101.  
  102.         if (min == 0) return null;
  103.  
  104.         // get all paths of the min length and get correct one
  105.         Node target = paths.stream()
  106.                 .filter(x -> x.size() == min)
  107.                 .map(x -> x.get(x.size() - 1))
  108.                 .min(comparing(Node::getY).thenComparing(Node::getX))
  109.                 .get();
  110.  
  111.         // now find all paths from the found target square
  112.         // going BACK to the adjacent open squares around the
  113.         // moving unit
  114.         paths = new ArrayList<>();
  115.         List<Node> adj = getAdjacent(u.position);
  116.         for (Node each : adj) {
  117.             List<Node> path = bfs(target, each);
  118.             if (!path.isEmpty()) paths.add(path);
  119.         }
  120.  
  121.         // find min of shortest paths
  122.         int min2 = paths.stream()
  123.                 .mapToInt(List::size)
  124.                 .min().getAsInt();
  125.  
  126.         // find best choice of shortest
  127.         return paths.stream()
  128.                 .filter(x -> x.size() == min2)
  129.                 .map(x -> x.get(x.size() - 1))
  130.                 .min(comparing(Node::getY).thenComparing(Node::getX))
  131.                 .get();
  132.     }
  133.  
  134.     private List<Node> bfs(Node start, Node target) {
  135.         Queue<Node> queue = new LinkedList<>();
  136.         Set<Node> visited = new HashSet<>();
  137.         Map<Node, Node> parents = new HashMap<>();
  138.  
  139.         queue.add(start);
  140.  
  141.         while (!queue.isEmpty()) {
  142.             Node curr = queue.poll();
  143.             if (curr.equals(target)) {
  144.                 List<Node> path = new ArrayList<>();
  145.                 curr = target;
  146.                 while (!curr.equals(start)) {
  147.                     path.add(curr);
  148.                     curr = parents.get(curr);
  149.                 }
  150.                 path.add(start);
  151.                 Collections.reverse(path);
  152.                 return path;
  153.             }
  154.  
  155.             for (Node adj : getAdjacent(curr)) {
  156.                 if (!visited.contains(adj)) {
  157.                     queue.add(adj);
  158.                     parents.put(adj, curr);
  159.                     visited.add(adj);
  160.                 }
  161.             }
  162.         }
  163.         return new ArrayList<>();
  164.     }
  165.  
  166.     private void sortReadingOrder() {
  167.         units.sort(comparing(Unit::getY).thenComparing(Unit::getX));
  168.     }
  169.  
  170.     // get open adjacent squares to Node n
  171.     private List<Node> getAdjacent(Node n) {
  172.         List<Node> adj = new ArrayList<>();
  173.         for (Direction dir : Direction.values()) {
  174.             int nx = n.getX() + dir.getDx();
  175.             int ny = n.getY() + dir.getDy();
  176.             if (grid[nx][ny] == '.') {
  177.                 adj.add(new Node(nx, ny));
  178.             }
  179.         }
  180.         return adj;
  181.     }
  182.  
  183.  
  184.     private Unit getAdjacentEnemy(Unit u) {
  185.         List<Unit> adj = new ArrayList<>();
  186.         for (Direction dir : Direction.values()) {
  187.             int nx = u.position.getX() + dir.getDx();
  188.             int ny = u.position.getY() + dir.getDy();
  189.             if (unitMap[nx][ny] != null
  190.                     && unitMap[nx][ny].elf == !u.elf) {
  191.                 adj.add(unitMap[nx][ny]);
  192.             }
  193.         }
  194.         if (adj.size() == 0) return null;
  195.         if (adj.size() == 1) return adj.get(0);
  196.         return adj.stream()
  197.                 .min(comparing(Unit::getHitPoints).thenComparing(Unit::getY)
  198.                 .thenComparing(Unit::getX)).get();
  199.     }
  200.  
  201.  
  202.  
  203.     private int round() {
  204.         //display();
  205.         sortReadingOrder();
  206.         for (Unit each : units) {
  207.             if (each.alive) {
  208.                 boolean done = each.turn();
  209.                 if (done) {
  210.                     //System.out.println(rounds);
  211.                     return rounds * units.stream()
  212.                             .filter(Unit::isAlive)
  213.                             .mapToInt(Unit::getHitPoints)
  214.                             .sum();
  215.                 }
  216.             }
  217.         }
  218.         rounds++;
  219.         return 0;
  220.     }
  221.  
  222.     @Override
  223.     public Object part1() {
  224.         int result = 0;
  225.         while (result == 0) {
  226.             result = round();
  227.         }
  228.         return result;
  229.     }
  230.  
  231.     @Override
  232.     public Object part2() {
  233.         AtomicInteger boost = new AtomicInteger(4);
  234.         while (true) {
  235.             parse();
  236.             int elves = (int) units.stream()
  237.                     .filter(Unit::isElf)
  238.                     .count();
  239.             rounds = 0;
  240.             units.stream()
  241.                     .filter(Unit::isElf)
  242.                     .forEach(x -> x.attackPower = boost.get());
  243.             int result = 0;
  244.             while (result == 0) {
  245.                 result = round();
  246.             }
  247.             if (units.stream()
  248.                     .filter(Unit::isElf)
  249.                     .filter(Unit::isAlive)
  250.                     .count() == elves) {
  251.                 return result;
  252.             }
  253.             boost.incrementAndGet();
  254.         }
  255.     }
  256.  
  257.     private void display() {
  258.         for (int i = 0; i < grid.length; i++) {
  259.             for (int j = 0; j < grid[i].length; j++) {
  260.                 System.out.print((char) grid[j][i]);
  261.             }
  262.             System.out.println();
  263.         }
  264.     }
  265.  
  266.     @Override
  267.     public void parse() {
  268.         grid = new int[input.size()][input.get(0).length()];
  269.         unitMap = new Unit[input.size()][input.get(0).length()];
  270.         units = new ArrayList<>();
  271.         for (int i = 0; i < input.size(); i++) {
  272.             String line = input.get(i);
  273.             for (int j = 0; j < line.length(); j++) {
  274.                 char c = line.charAt(j);
  275.                 if (c == 'G' || c == 'E') {
  276.                     Unit unit = new Unit();
  277.                     unitMap[j][i] = unit;
  278.                     unit.position = new Node(j, i);
  279.                     unit.elf = c == 'E';
  280.                     units.add(unit);
  281.                 }
  282.                 grid[j][i] = c;
  283.             }
  284.         }
  285.     }
  286. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement