Skip to content

Commit

Permalink
Cleaning solution for 2023, Day 11
Browse files Browse the repository at this point in the history
  • Loading branch information
zodac committed Dec 14, 2023
1 parent a84246d commit 87b7770
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 142 deletions.
195 changes: 58 additions & 137 deletions 2023/src/main/java/me/zodac/advent/Day11.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@
package me.zodac.advent;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import me.zodac.advent.pojo.Point;
import me.zodac.advent.pojo.grid.CharacterGrid;
import me.zodac.advent.util.CollectionUtils;

/**
* Solution for 2023, Day 11.
Expand All @@ -34,165 +31,89 @@
*/
public final class Day11 {

private Day11() {
private static final char GALAXY_SYMBOL = '#';

}
private Day11() {

private static <E> List<E> findValuesLessThan(final Collection<E> values, final E value, final Comparator<? super E> comparator) {
return values
.stream()
.filter(v -> Objects.compare(value, v, comparator) == 1)
.toList();
}

/**
* Part 1.
* Given a {@link List} of {@link String}s representing space, each {@link #GALAXY_SYMBOL} represents a galaxy. In order to account for the
* expansion of space-time, any row or column that does not contain a galaxy needs to be replaced with {@code expansionSize} number of empty rows
* or columns.
*
* @param values the input values
* @return the part 1 result
* <p>
* Once the expansion is complete, we count the distance from each galaxy to every other galaxy (in either direction, so only count the distance
* from galaxy A -> galaxy B once time), then sum up all these distances.
*
* @param values the input space map
* @param expansionSize the number of additional rows/columns to add to any empty rows/columns
* @return the total distance between all galaxies
*/
public static long part1(final List<String> values, final int expansionSize) {
final List<List<Character>> grid = new ArrayList<>();
final List<Integer> emptyRows = new ArrayList<>();
final List<Integer> emptyColumns = new ArrayList<>();

for (final String value : values) {
final List<Character> chars = new ArrayList<>();
for (final char ch : value.toCharArray()) {
chars.add(ch);
}
public static long sumOfDistancesBetweenGalaxies(final List<String> values, final int expansionSize) {
final CharacterGrid characterGrid = CharacterGrid.parse(values);
final List<Integer> emptyRows = findRowsWithoutGalaxies(characterGrid);
final List<Integer> emptyColumns = findColumnsWithoutGalaxies(characterGrid);
final int actualExpansionSize = expansionSize - 1; // We are replacing rows/columns, not adding, so we don't count the existing ones

grid.add(chars);
}

for (int i = 0; i < grid.size(); i++) {
final List<Character> row = grid.get(i);
boolean hasNoGalaxies = true;

for (final Character value : row) {
if (value == '#') {
hasNoGalaxies = false;
break;
}
}

if (hasNoGalaxies) {
emptyRows.add(i);
}
}

for (int j = 0; j < grid.getFirst().size(); j++) {
boolean hasNoGalaxies = true;

for (final List<Character> characters : grid) {
final char value = characters.get(j);
if (value == '#') {
hasNoGalaxies = false;
break;
}
}

if (hasNoGalaxies) {
emptyColumns.add(j);
}
}

final List<Point> galaxies = new ArrayList<>();

for (int i = 0; i < grid.size(); i++) {
final List<Character> row = grid.get(i);

for (int j = 0; j < row.size(); j++) {
final char val = row.get(j);
if (val == '#') {
final int extraColumns = (findValuesLessThan(emptyColumns, j, Integer::compareTo).size() * (expansionSize-1));
final int extraRows = (findValuesLessThan(emptyRows, i, Integer::compareTo).size() * (expansionSize-1));
galaxies.add(Point.of(i + extraRows, j + extraColumns));
}
}
}
final List<Point> galaxies = findGalaxies(characterGrid, emptyColumns, emptyRows, actualExpansionSize);

long total = 0L;

for (int i = 0; i < galaxies.size(); i++) {
final Point currentGalaxy = galaxies.get(i);

for (int j = i + 1; j < galaxies.size(); j++) {
// final long val = pathExists(expandedGrid, currentGalaxy, galaxies.get(j));
final long val = calculateDistance(currentGalaxy, galaxies.get(j));
// System.out.printf("Galaxy #%d -> #%d: %d%n", (i + 1), (j + 1), val);
if (val != -1) {
total += val;
}
}
total += galaxies.subList(i, galaxies.size())
.stream()
.mapToLong(currentGalaxy::distanceTo)
.sum();
}

return total;
}

private static long calculateDistance(final Point first, final Point second) {
return Math.abs(first.x() - second.x()) + Math.abs(first.y() - second.y());
}

private static Character[][] deepCopy(final Character[][] input) {
return Arrays.stream(input)
.map(Character[]::clone)
.toArray(array -> input.clone());
}

private static int pathExists(Character[][] input, final Point start, final Point target) {
final Character[][] matrix = deepCopy(input);
Node source = new Node(start.x(), start.y(), 0);
Queue<Node> queue = new LinkedList<Node>();

int numOfRows = matrix.length;
int numOfColumns = matrix[0].length;

queue.add(source);

while (!queue.isEmpty()) {
Node popped = queue.poll();
private static List<Point> findGalaxies(final CharacterGrid characterGrid,
final Collection<Integer> emptyColumns,
final Collection<Integer> emptyRows,
final int expansionSize) {
final List<Point> galaxies = new ArrayList<>();

if (popped.x == target.x() && popped.y == target.y()) {
return popped.distanceFromSource;
} else {
matrix[popped.x][popped.y] = '0';
for (int rowIndex = 0; rowIndex < characterGrid.numberOfRows(); rowIndex++) {
final Character[] row = characterGrid.getRow(rowIndex);

List<Node> neighbourList = addNeighbours(popped, matrix, numOfRows, numOfColumns);
queue.addAll(neighbourList);
for (int columnIndex = 0; columnIndex < row.length; columnIndex++) {
final char val = row[columnIndex];
if (val == GALAXY_SYMBOL) {
final Point galaxy = createGalaxyPoint(emptyRows, emptyColumns, rowIndex, columnIndex, expansionSize);
galaxies.add(galaxy);
}
}
}
return -1;
return galaxies;
}

private static List<Node> addNeighbours(Node poped, Character[][] matrix, final int numOfRows, final int numOfColumns) {

List<Node> list = new LinkedList<Node>();

if ((poped.x - 1 >= 0 && poped.x - 1 < numOfRows) && matrix[poped.x - 1][poped.y] != '0') {
list.add(new Node(poped.x - 1, poped.y, poped.distanceFromSource + 1));
}
if ((poped.x + 1 >= 0 && poped.x + 1 < numOfRows) && matrix[poped.x + 1][poped.y] != '0') {
list.add(new Node(poped.x + 1, poped.y, poped.distanceFromSource + 1));
}
if ((poped.y - 1 >= 0 && poped.y - 1 < numOfColumns) && matrix[poped.x][poped.y - 1] != '0') {
list.add(new Node(poped.x, poped.y - 1, poped.distanceFromSource + 1));
}
if ((poped.y + 1 >= 0 && poped.y + 1 < numOfColumns) && matrix[poped.x][poped.y + 1] != '0') {
list.add(new Node(poped.x, poped.y + 1, poped.distanceFromSource + 1));
}
return list;
private static Point createGalaxyPoint(final Collection<Integer> emptyRows,
final Collection<Integer> emptyColumns,
final int rowIndex,
final int columnIndex,
final int expansionSize) {
final int emptyColumnsToAdd = CollectionUtils.findValuesLessThan(emptyColumns, columnIndex, Integer::compareTo).size();
final int emptyRowsToAdd = CollectionUtils.findValuesLessThan(emptyRows, rowIndex, Integer::compareTo).size();

final int extraColumns = emptyColumnsToAdd * expansionSize;
final int extraRows = emptyRowsToAdd * expansionSize;
return Point.of(rowIndex + extraRows, columnIndex + extraColumns);
}

static class Node {
int x;
int y;
int distanceFromSource;
private static List<Integer> findColumnsWithoutGalaxies(final CharacterGrid characterGrid) {
return characterGrid
.findColumnsWith(character -> character != GALAXY_SYMBOL)
.toList();
}

Node(int x, int y, int dis) {
this.x = x;
this.y = y;
this.distanceFromSource = dis;
}
private static List<Integer> findRowsWithoutGalaxies(final CharacterGrid characterGrid) {
return characterGrid
.findRowsWith(character -> character != GALAXY_SYMBOL)
.toList();
}
}
10 changes: 5 additions & 5 deletions 2023/src/test/java/me/zodac/advent/Day11Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ public class Day11Test {
void example() {
final List<String> values = ExampleInput.readLines(INPUT_FILENAME);

final long part1Result = Day11.part1(values, 2);
final long part1Result = Day11.sumOfDistancesBetweenGalaxies(values, 2);
assertThat(part1Result)
.isEqualTo(374L);

final long part2Result = Day11.part1(values, 10);
final long part2Result = Day11.sumOfDistancesBetweenGalaxies(values, 10);
assertThat(part2Result)
.isEqualTo(1_030L);

final long part2Result2 = Day11.part1(values, 100);
final long part2Result2 = Day11.sumOfDistancesBetweenGalaxies(values, 100);
assertThat(part2Result2)
.isEqualTo(8_410L);
}
Expand All @@ -52,7 +52,7 @@ void example() {
void part1() {
final List<String> values = PuzzleInput.readLines(INPUT_FILENAME);

final long part1Result = Day11.part1(values, 2);
final long part1Result = Day11.sumOfDistancesBetweenGalaxies(values, 2);
assertThat(part1Result)
.isEqualTo(10_289_334L);
}
Expand All @@ -61,7 +61,7 @@ void part1() {
void part2() {
final List<String> values = PuzzleInput.readLines(INPUT_FILENAME);

final long part2Result = Day11.part1(values, 1_000_000);
final long part2Result = Day11.sumOfDistancesBetweenGalaxies(values, 1_000_000);
assertThat(part2Result)
.isEqualTo(649_862_989_626L);
}
Expand Down
79 changes: 79 additions & 0 deletions common-utils/src/main/java/me/zodac/advent/pojo/grid/Grid.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@

package me.zodac.advent.pojo.grid;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import me.zodac.advent.pojo.Point;
import me.zodac.advent.util.ArrayUtils;

Expand Down Expand Up @@ -317,6 +323,61 @@ public Set<Point> findValue(final E wantedValue) {
return points;
}

/**
* Iterates through all columns of the {@link Grid}, and checks whether they pass the provided {@link Predicate}. If they pass, the index of that
* column is returned.
*
* @param predicate the {@link Predicate} to test all columns against
* @return a {@link Stream} of indexes of all matching columns
*/
public Stream<Integer> findColumnsWith(final Predicate<? super E> predicate) {
final Collection<Integer> matchingColumns = new ArrayList<>();
for (int i = 0; i < internalGrid[0].length; i++) {
boolean match = true;

for (final E[] characters : internalGrid) {
final E value = characters[i];
if (!predicate.test(value)) {
match = false;
break;
}
}

if (match) {
matchingColumns.add(i);
}
}
return matchingColumns.stream();
}

/**
* Iterates through all rows of the {@link Grid}, and checks whether they pass the provided {@link Predicate}. If they pass, the index of that
* row is returned.
*
* @param predicate the {@link Predicate} to test all rows against
* @return a {@link Stream} of indexes of all matching rows
*/
public Stream<Integer> findRowsWith(final Predicate<? super E> predicate) {
final Collection<Integer> matchingRows = new HashSet<>();
for (int i = 0; i < internalGrid.length; i++) {
final E[] row = internalGrid[i];
boolean match = true;

for (final E value : row) {
if (!predicate.test(value)) {
match = false;
break;
}
}

if (match) {
matchingRows.add(i);
}

}
return matchingRows.stream();
}

/**
* Prints the content of the {@link Grid}.
*
Expand Down Expand Up @@ -352,4 +413,22 @@ public void print(final boolean withHeaders) {
System.out.println(); // NOPMD
}
}

@Override
public boolean equals(final Object object) {
if (this == object) {
return true;
}

if (!(object instanceof final Grid<?> otherGrid)) {
return false;
}

return Arrays.deepEquals(internalGrid, otherGrid.internalGrid);
}

@Override
public int hashCode() {
return Arrays.deepHashCode(internalGrid);
}
}

0 comments on commit 87b7770

Please sign in to comment.