diff --git a/2022/README.md b/2022/README.md index 9781814395d1adfd6fd0aa94c8760e1708c8447a..c8dbb381f4c280d8ff3c60e2aaad92b55ce89ad0 100644 --- a/2022/README.md +++ b/2022/README.md @@ -580,6 +580,15 @@ The subproblems are - Determine which positions cannot contain a beacon (*i.e.*, which positions are no closer to the sensor than the beacon) +Creating many, many beacons would seem to be untenable from a memory perspective. +I think we're going to have to dynamically build the map and then forget which sensor detected which beacon. +This is going to be a PITA Linked List of Linked Lists. +*Or*, I could make two passes, one to determine the matrix's dimensions, and one to populate it. + +Okay, the problem (unsurprisingly) wasn't all the Sensor data; it was the 6,862,736 x 6,004,906 matrix. +What? You don't have several terabytes just lying around? +Clearly we need to go *back* to tracking the Sensor data and dynamically determine whether a given position is covered. + ### Part 2 ... diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java index 8cf52ca662f0f559b819c949f99511e7c9653829..dfbed894fe965b2fce94748fdc4578ce17e84c86 100644 --- a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java +++ b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java @@ -2,13 +2,12 @@ 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") +@SuppressWarnings({"unused"}) public class Day15 extends Puzzle { public Day15(boolean isProductionReady) { super(isProductionReady); @@ -34,12 +33,8 @@ public class Day15 extends Puzzle { for (String datum : data) { new Sensor(datum); } - Sensor.markPositionsAsHavingNoUnknownBeacon(); -// Sensor.printMap(); -// System.out.println("==="); //noinspection MagicNumber - return Sensor.countPositionsWithNoUnknownBeacon(isProductionReady ? 2000000 : 10); -// return 0; + return Sensor.countLocationsThatCannotHaveBeacon(isProductionReady ? 2000000 : 10); } @Override @@ -53,8 +48,11 @@ public class Day15 extends Puzzle { private static final Set<Sensor> sensors = new HashSet<>(); - private static boolean[][] mightContainAnUnknownBeacon = null; - private static int xOffset, yOffset; + // private static boolean[][] mightContainAnUnknownBeacon = null; + private static int minimumX = Integer.MAX_VALUE; + private static int maximumX = Integer.MIN_VALUE; + private static int minimumY = Integer.MAX_VALUE; + private static int maximumY = Integer.MIN_VALUE; public Sensor(String description) { String[] hardwareDescriptions = description.split(":"); @@ -67,6 +65,11 @@ public class Day15 extends Puzzle { nearestBeaconY = Integer.parseInt(coordinateDescriptions[1].substring(coordinateDescriptions[1].indexOf('=') + 1)); sensors.add(this); + int distance = computeManhattanDistance(x, y, nearestBeaconX, nearestBeaconY); + minimumX = Math.min(minimumX, x - distance); + minimumY = Math.min(minimumY, y - distance); + maximumX = Math.max(maximumX, x + distance); + maximumY = Math.max(maximumY, y + distance); } public int getDistanceToBeacon() { @@ -74,7 +77,7 @@ public class Day15 extends Puzzle { } private int getDistanceToLocation(int x, int y) { - return Math.abs(x - this.x) + Math.abs(y - this.y); + return computeManhattanDistance(this.x, this.y, x, y); } public boolean isNoFartherThanBeacon(int x, int y) { @@ -87,93 +90,37 @@ public class Day15 extends Puzzle { + ", y=" + nearestBeaconY + "\t(distance=" + getDistanceToBeacon() + ")"; } + public static int computeManhattanDistance(int x1, int y1, int x2, int y2) { + return Math.abs(x1 - x2) + Math.abs(y1 - y2); + } + public static Sensor getSensorAt(int x, int y) { return sensors.stream().filter(s -> s.x == x && s.y == y).findFirst().orElse(null); } - public static Sensor getSensorDetectingBeaconAt(int x, int y ) { + public static boolean aSensorIsAt(int x, int y) { + return sensors.stream().anyMatch(s -> s.x == x && s.y == y); + } + + public static Sensor getSensorDetectingBeaconAt(int x, int y) { return sensors.stream() .filter(s -> s.nearestBeaconX == x && s.nearestBeaconY == y).findFirst().orElse(null); } - public static void markPositionsAsHavingNoUnknownBeacon() { - // create the matrix - int maximumX = sensors.stream() - .mapToInt(sensor -> sensor.x + sensor.getDistanceToBeacon()) - .max().orElse(Integer.MIN_VALUE); - int minimumX = sensors.stream() - .mapToInt(sensor -> sensor.x - sensor.getDistanceToBeacon()) - .min().orElse(Integer.MAX_VALUE); - int maximumY = sensors.stream() - .mapToInt(sensor -> sensor.y + sensor.getDistanceToBeacon()) - .max().orElse(Integer.MIN_VALUE); - int minimumY = sensors.stream() - .mapToInt(sensor -> sensor.y - sensor.getDistanceToBeacon()) - .min().orElse(Integer.MAX_VALUE); - xOffset = minimumX; - yOffset = minimumY; - mightContainAnUnknownBeacon = new boolean[maximumY - yOffset + 1][maximumX - xOffset + 1]; - IntStream.range(0, mightContainAnUnknownBeacon.length) - .forEach(i -> Arrays.fill(mightContainAnUnknownBeacon[i], true)); - // make our deductions - for (int i = 0; i < mightContainAnUnknownBeacon.length; i++) { - for (int j = 0; j < mightContainAnUnknownBeacon[i].length; j++) { - int y = i + yOffset, x = j + xOffset; - for (Sensor sensor : sensors) { - if (sensor.isNoFartherThanBeacon(x, y)) { - mightContainAnUnknownBeacon[i][j] = false; - } - } - } - } + public static boolean aKnownBeaconIsAt(int x, int y) { + return sensors.stream().anyMatch(s -> s.nearestBeaconX == x && s.nearestBeaconY == y); } - public static int countPositionsWithNoUnknownBeacon(int y) { - if (mightContainAnUnknownBeacon == null) { - markPositionsAsHavingNoUnknownBeacon(); - } -// System.out.print(xOffset + " "); - int countOfObservedLocations = 0; - int countOfKnownBeacons = 0; - int i = y - yOffset; - boolean[] row = mightContainAnUnknownBeacon[i]; - for (int j = 0; j < row.length; j++) { - int x = j + xOffset; -// System.out.print(row[j] ? '.' : "#"); - countOfObservedLocations += row[j] ? 0 : 1; - countOfKnownBeacons += (getSensorDetectingBeaconAt(x,y) == null) ? 0 : 1; - } -// System.out.println(" " + (mightContainAnUnknownBeacon[y - yOffset].length - xOffset - 1)); - return countOfObservedLocations - countOfKnownBeacons; + public static boolean isCoveredBySensor(int x, int y) { + return sensors.stream().anyMatch(sensor -> sensor.isNoFartherThanBeacon(x,y)); } - public static void printMap() { - if (mightContainAnUnknownBeacon == null) { - markPositionsAsHavingNoUnknownBeacon(); - } - System.out.println("xOffset = " + xOffset + "\tyOffset = " + yOffset); - for (int i = 0; i < mightContainAnUnknownBeacon.length; i++) { - int y = i + yOffset; - for (int j = 0; j < mightContainAnUnknownBeacon[i].length; j++) { - int x = j + xOffset; - if (getSensorAt(x,y) != null) { - System.out.print("S"); - } else if (getSensorDetectingBeaconAt(x,y) != null) { - System.out.print("B"); - } else if (mightContainAnUnknownBeacon[i][j]) { - System.out.print("."); - } else { - System.out.print("#"); - } - if (x % 5 == 4 || x == -1) { - System.out.print(" "); - } - } - System.out.println(); - if (y % 5 == 4 || y == -1) { - System.out.println(); - } - } + public static int countLocationsThatCannotHaveBeacon(int y) { + int locationsInRangeOfSensor + = IntStream.rangeClosed(minimumX, maximumX).map(x -> isCoveredBySensor(x, y) ? 1 : 0).sum(); + int locationsWithKnownBeacon = + IntStream.rangeClosed(minimumX, maximumX).map(x -> aKnownBeaconIsAt(x, y) ? 1 : 0).sum(); + return locationsInRangeOfSensor - locationsWithKnownBeacon; } } }