diff --git a/2022/README.md b/2022/README.md index 8ca785acb2b8f5c76d679b80545213234ce68f34..4f46e0d82fc9fd9cfacf50d227e7cc8e8f763239 100644 --- a/2022/README.md +++ b/2022/README.md @@ -654,9 +654,39 @@ No. That solves the memory problem but not the time problem. Even if we could process 4 billion rocks per second (which we can't) then it would take a little over 4 minutes to process 1 trillion rocks, and AOC solutions shouldn't take that long. +There is almost certainly a cycle (with a period that is a multiple of both the number of rocks and the length of the +jet string?) -- if we can detect the cycle then we can determine the height as (height of prefix) + +(number of cycles) * (height of a cycle) + (height of suffix). Maybe later. + ... ## Day 18 +- [The problem](https://adventofcode.com/2022/day/18) +- [The solution](src/main/java/edu/unl/cse/bohn/year2022/Day18.java) + +### Part 1 + +The subproblems are +- Parse the input to determine where each cube is in space +- For each cube, determine which faces are adjacent to another cube +- Count the number of faces that are not adjacent to another cube + +Determining whether a face is adjacent to another cube should be straight-forward. +Given axes *i*, *j*, and *k*, cube *c1* is adjacent to cube *c2* iff wlog *c1.i* = *c2.i* and *c1.j* = *c2.j* and +*c1.k* = *c2.k* ± 1. + +### Part 2 + +We need to determine how many cavities are present in the droplet. +We can determine that by eliminating everything that is *not* a cavity. + +If we consider a bounding box, then every cube is either: +- Lava, as computed in part 1 +- Exterior air, which has no lava between it and the box's bound in at least one direction +- A cavity + +## Day 19 + (coming soon) diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day18.java b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day18.java new file mode 100644 index 0000000000000000000000000000000000000000..3beae53a29d9d838b559a8de1ec39bcece3d4baf --- /dev/null +++ b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day18.java @@ -0,0 +1,177 @@ +package edu.unl.cse.bohn.year2022; + +import edu.unl.cse.bohn.Puzzle; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.IntStream; + +@SuppressWarnings("unused") +public class Day18 extends Puzzle { + @SuppressWarnings("CommentedOutCode") + public Day18(boolean isProductionReady) { + super(isProductionReady); +// sampleData = """ +// 1,1,1 +// 2,1,1"""; + sampleData = """ + 2,2,2 + 1,2,2 + 3,2,2 + 2,1,2 + 2,3,2 + 2,2,1 + 2,2,3 + 2,2,4 + 2,2,6 + 1,2,5 + 3,2,5 + 2,1,5 + 2,3,5"""; + } + + @Override + public long computePart1(List<String> data) { + for (String description : data) { + new Cube(description); + } + for (Cube cube1 : Cube.lava) { + for (Cube cube2 : Cube.lava) { + cube1.checkIfAdjacentTo(cube2); + } + } + return Cube.lava.stream().mapToLong(Cube::countExposedFaces).sum(); + } + + @Override + public long computePart2(List<String> data) { + // assume (correctly) that lava has already been created + int upperBound = Cube.maximumLength + 2; // plenty + for (int i = 0; i < upperBound; i++) { + for (int j = 0; j < upperBound; j++) { + for (int k = 0; k < upperBound; k++) { + new Cube(i, j, k); + } + } + } + Cube.removeExteriorAir(); + for (Cube lavaCube : Cube.lava) { + for (Cube airCube : Cube.air) { + lavaCube.checkIfAdjacentTo(airCube); + } + } + return Cube.lava.stream().mapToLong(Cube::countExposedFaces).sum(); + } + + private static class Cube { + public static final Set<Cube> lava = new HashSet<>(); + public static final Set<Cube> air = new HashSet<>(); + static int maximumLength = Integer.MIN_VALUE; + + private final int[] coordinates; + private final boolean[] faceIsExposed; + + public Cube(String lavaDescription) { + // use for lava + coordinates = Arrays.stream(lavaDescription.split(",")).mapToInt(Integer::parseInt).toArray(); + faceIsExposed = new boolean[]{true, true, true, true, true, true}; + maximumLength = Math.max(maximumLength, Arrays.stream(coordinates).max().orElseThrow()); + air.remove(this); + lava.add(this); + } + + public Cube(int x, int y, int z) { + // use for air + coordinates = new int[]{x, y, z}; + faceIsExposed = new boolean[]{true, true, true, true, true, true}; + if (!lava.contains(this)) { + air.add(this); + } + } + + public static void removeExteriorAir() { + Set<Cube> exteriorAir = new HashSet<>(); + for (Cube airCube : air) { + for (Cube lavaCube : lava) { + if (airCube.isAlignedInTwoDimensionsWith(lavaCube, 1, 2) + && airCube.distanceInOneDimension(lavaCube, 0) != 0) { + airCube.faceIsExposed[airCube.distanceInOneDimension(lavaCube, 0) < 0 ? 0 : 1] = false; + } + if (airCube.isAlignedInTwoDimensionsWith(lavaCube, 0, 2) + && airCube.distanceInOneDimension(lavaCube, 1) != 0) { + airCube.faceIsExposed[2 + (airCube.distanceInOneDimension(lavaCube, 1) < 0 ? 0 : 1)] = false; + } + if (airCube.isAlignedInTwoDimensionsWith(lavaCube, 0, 1) + && airCube.distanceInOneDimension(lavaCube, 2) != 0) { + airCube.faceIsExposed[4 + (airCube.distanceInOneDimension(lavaCube, 2) < 0 ? 0 : 1)] = false; + } + } + if (airCube.countExposedFaces() > 0) { + exteriorAir.add(airCube); + } + } + air.removeAll(exteriorAir); + // if we stopped now, then air in twisty passages would be treated as cavities + // any air that is adjacent to exterior air must also be exterior air + Set<Cube> newlyDiscoveredExteriorAir; + do { + newlyDiscoveredExteriorAir = new HashSet<>(); + for (Cube cavityCube : air) { + for (Cube exteriorCube : exteriorAir) { + if (cavityCube.checkIfAdjacentTo(exteriorCube)) { + newlyDiscoveredExteriorAir.add(cavityCube); + } + } + } + exteriorAir.addAll(newlyDiscoveredExteriorAir); + air.removeAll(newlyDiscoveredExteriorAir); + } while (!newlyDiscoveredExteriorAir.isEmpty()); + } + + private int distanceInOneDimension(Cube other, int dimension) { + return other.coordinates[dimension] - this.coordinates[dimension]; + } + + private boolean isAlignedInTwoDimensionsWith(Cube other, int dimension1, int dimension2) { + return distanceInOneDimension(other, dimension1) == 0 && distanceInOneDimension(other, dimension2) == 0; + } + + @SuppressWarnings("UnusedReturnValue") + private boolean checkIfAdjacentTo(Cube other) { + if (isAlignedInTwoDimensionsWith(other, 1, 2) + && Math.abs(distanceInOneDimension(other, 0)) == 1) { + faceIsExposed[(1 + distanceInOneDimension(other, 0)) / 2] = false; + return true; + } + if (isAlignedInTwoDimensionsWith(other, 0, 2) + && Math.abs(distanceInOneDimension(other, 1)) == 1) { + faceIsExposed[2 + (1 + distanceInOneDimension(other, 1)) / 2] = false; + return true; + } + if (isAlignedInTwoDimensionsWith(other, 0, 1) + && Math.abs(distanceInOneDimension(other, 2)) == 1) { + faceIsExposed[4 + (1 + distanceInOneDimension(other, 2)) / 2] = false; + return true; + } + return false; + } + + private long countExposedFaces() { + return IntStream.range(0, faceIsExposed.length).filter(i -> faceIsExposed[i]).count(); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (!(other instanceof Cube cube)) return false; + return Arrays.equals(coordinates, cube.coordinates); + } + + @Override + public int hashCode() { + return Arrays.hashCode(coordinates); + } + } +}