aoc2024

Advent of Code 2024
Log | Files | Refs | README

commit b4586e83a0e12f8a320c8625eff2288860a36618
parent d12876e85c885c2f3e6e00241272241689e33960
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Sat,  4 Jan 2025 16:31:00 +0000

Day 15 Pt2

Diffstat:
Asrc/main/java/day15_pt2.java | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 365 insertions(+), 0 deletions(-)

diff --git a/src/main/java/day15_pt2.java b/src/main/java/day15_pt2.java @@ -0,0 +1,365 @@ +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.IOException; +import java.util.*; +import java.util.stream.*; +import java.util.regex.*; + + +class day15_pt2 { + + record Point(int x, int y) implements Comparable<Point> { + Point add(Vec v) { + return new Point(this.x + v.x, this.y + v.y); + } + @Override + public int compareTo(Point other) { + var res = Integer.valueOf(this.x).compareTo(Integer.valueOf(other.x)); + if (res != 0) return res; + return Integer.valueOf(this.y).compareTo(Integer.valueOf(other.y)); + } + } + record Vec(int x, int y) {} + record Box(int label) {} + enum Direction{ + North('^'),East('>'),South('v'),West('<'); + final char c; + Direction(char c) { this.c = c; } + + Vec unitVec() { + return switch (this) { + case North -> new Vec(0, -1); + case East -> new Vec(1, 0) ; + case South -> new Vec(0, 1); + case West -> new Vec(-1, 0); + }; + } + static Direction from(char c) { + for (var d: Direction.values()) { + if (d.c == c) return d; + } + throw new IllegalArgumentException("unrecognised direction %c".formatted(c)); + } + } + public static void main(String[] args) throws IOException { + var input = Files.readAllLines(Path.of(args[0])); + var spl = input.indexOf(""); + var grid = input.subList(0, spl); + + for (var y=0;y<grid.size();y++) { + var line = grid.get(y); + var sb = new StringBuilder(); + for (var ch : line.chars().toArray()) { + switch (ch) { + case '@': + sb.append("@."); + break; + case '#': + sb.append("##"); + break; + //tehsoieshfeishfe + case 'O': + sb.append("[]"); + break; + case '.': + sb.append(".."); + break; + } + } + grid.set(y, sb.toString()); + } + + // for (var line : grid) { + // System.out.println(line); + // } + + var inst = input.subList(spl+1, input.size()).stream().collect(Collectors.joining("")); + var height = grid.size(); + var width = grid.get(0).length(); + Point robotPos = null; + var boxes = new MSet<Box>(); + var boxLabel = 0; + var boxPositions = new BMap<Point,Box>(); + var walls = new MSet<Point>(); + for (var y=0;y<height;y++) { + for (var x=0;x<width;x++) { + var p = new Point(x, y); + var val = gv(grid, p); + if (val == '@') { + robotPos = p; + } else if (val == '[') { + var box = new Box(boxLabel++); + boxes.add(box); + boxPositions.put(p, box); + boxPositions.put(p.add(Direction.East.unitVec()), box); + } else if (val == '#') { + walls.add(p); + } + } + } + //System.out.printf("RobotPos: %s\n", robotPos); + for (var ch : inst.chars().toArray()) { + var d = Direction.from((char)ch); + var np = robotPos.add(d.unitVec()); + if (walls.contains(np)) { + // do nothing + } else if (boxPositions.containsKey(np)) { + var box = boxPositions.get(np); + var newBoxPositions = moveBox(box, d, boxPositions, walls); + if (newBoxPositions.isEmpty()) { + // do nothing + } else { + // Replace the moved boxes in the boxPositions map + var vals = newBoxPositions.get().values(); + vals.forEach(boxPositions::reverseRemove); + boxPositions.putAll(newBoxPositions.get()); + robotPos = np; + } + } else { + robotPos = np; + } + // Debugging... + // System.out.printf("instruction: %c\n", ch); + // for (var y=0;y<height;y++) { + // for (var x=0;x<width;x++) { + // var p = new Point(x, y); + // if (walls.contains(p)) { + // System.out.print('#'); + // } else if (boxPositions.containsKey(p)) { + // System.out.print("[]"); + // x++; // skip te next we know what it is + // } else if (p.equals(robotPos)) { + // System.out.print('@'); + // } else { + // System.out.print('.'); + // } + // } + // System.out.print('\n'); + // } + // System.out.print('\n'); + } + var score = boxes.stream() + .map(boxPositions::reverseGet) + .map(pts -> { + var al = new ArrayList<>(pts); + Collections.sort(al); + return al.get(0); + }) + .mapToLong(pt -> pt.x + 100 * pt.y) + .sum(); + System.out.printf("Day 15 pt2: %d\n", score); + } + // Returns map of _new_ box positions of any boxes that moved... + static Optional<BMap<Point,Box>> moveBox(Box box, + Direction d, + BMap<Point, Box> boxPositions, + MSet<Point> walls) { + //System.out.printf("trying to move box %s\n", box); + var bPos = boxPositions.reverseGet(box); + var bNP = bPos.stream().map(p -> p.add(d.unitVec())).collect(Collectors.toSet()); + if (walls.containsAny(bNP)) { + return Optional.empty(); + } else { + var collidingBoxes = boxPositions.getMulti(bNP).stream() + .filter(b2 -> !b2.equals(box)) + .collect(Collectors.toSet()); + if (collidingBoxes.isEmpty()) { + var newPositions = new BMap<Point, Box>(); + bNP.forEach(pp -> newPositions.put(pp, box)); + return Optional.of(newPositions); + } else { + return collidingBoxes.stream().map(b2 -> moveBox(b2, d, boxPositions, walls)) + .reduce((m1, m2) -> { + if (m1.isEmpty() || m2.isEmpty()) return Optional.empty(); + var res = new BMap<Point,Box>(); + res.putAll(m1.get()); + res.putAll(m2.get()); + return Optional.of(res); + }).get() + .map(newPositions -> { + bNP.forEach(pp -> newPositions.put(pp, box)); + return newPositions; + }); + } + } + } + + static int gv(List<String> grid, Point p) { + var height = grid.size(); + if (p.y < 0 || p.y >= height) return '#'; // treat out of bounds as a wall + var width = grid.get(0).length(); + if (p.x < 0 || p.x >= width) return '#'; + return grid.get(p.y).charAt(p.x); + } +} + +class BMap<K, V> implements Map<K, V> { + final Map<K, V> forward; + final Map<V, Set<K>> backward; + + public BMap() { + this.forward = new HashMap<>(); + this.backward = new HashMap<>(); + } + + @Override + public int size() { + return forward.size(); + } + + @Override + public boolean isEmpty() { + return forward.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return forward.containsKey(key); + } + + public boolean containsAnyKey(Collection<K> keys) { + return keys.stream().anyMatch(this::containsKey); + } + + @Override + public boolean containsValue(Object value) { + return backward.containsKey(value); + } + + @Override + public V get(Object key) { + return forward.get(key); + } + + public Set<V> getMulti(Collection<K> keys) { + return keys.stream().map(this::get).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + @Override + public V put(K key, V value) { + var res = forward.put(key, value); + backward.computeIfAbsent(value, k -> new HashSet<>()).add(key); + return res; + } + + @Override + public V remove(Object key) { + var res = forward.remove(key); + backward.computeIfAbsent(res, k -> new HashSet<>()).remove(key); + return res; + } + + @Override + public void putAll(Map<? extends K, ? extends V> m) { + m.forEach(this::put); + } + + @Override + public void clear() { + forward.clear(); + backward.clear(); + } + + @Override + public Set<K> keySet() { + return forward.keySet(); + } + + @Override + public Collection<V> values() { + return forward.values(); + } + + @Override + public Set<Entry<K, V>> entrySet() { + return forward.entrySet(); + } + + public Set<K> reverseGet(V value) { + return backward.get(value); + } + + public void reverseRemove(V value) { + var vals = backward.remove(value); + if (vals != null) { + vals.stream().forEach(forward::remove); + } + } +} + +class MSet<K> implements Set<K> { + private final Set<K> internal; + + public MSet() { + internal = new HashSet<>(); + } + + @Override + public int size() { + return internal.size(); + } + + @Override + public boolean isEmpty() { + return internal.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return internal.contains(o); + } + + @Override + public Iterator<K> iterator() { + return internal.iterator(); + } + + @Override + public Object[] toArray() { + return internal.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + return internal.toArray(a); + } + + @Override + public boolean add(K k) { + return internal.add(k); + } + + @Override + public boolean remove(Object o) { + return internal.remove(o); + } + + @Override + public boolean containsAll(Collection<?> c) { + return internal.containsAll(c); + } + + public boolean containsAny(Collection<? extends K> c) { + return c.stream().anyMatch(internal::contains); + } + + @Override + public boolean addAll(Collection<? extends K> c) { + return internal.addAll(c); + } + + @Override + public boolean retainAll(Collection<?> c) { + return internal.retainAll(c); + } + + @Override + public boolean removeAll(Collection<?> c) { + return internal.removeAll(c); + } + + @Override + public void clear() { + internal.clear(); + } +}