|
1 | 1 | package dev.jacobandersen.codechallenges.challenge.adventofcode.year2024.day15;
|
2 | 2 |
|
3 | 3 | import dev.jacobandersen.codechallenges.challenge.adventofcode.Day;
|
| 4 | +import dev.jacobandersen.codechallenges.util.Direction; |
| 5 | +import dev.jacobandersen.codechallenges.util.Grid; |
| 6 | +import dev.jacobandersen.codechallenges.util.Pair; |
| 7 | + |
| 8 | +import java.util.ArrayList; |
| 9 | +import java.util.Arrays; |
| 10 | +import java.util.List; |
| 11 | +import java.util.concurrent.atomic.AtomicInteger; |
4 | 12 |
|
5 | 13 | public class Day15 extends Day {
|
| 14 | + private static final String ROBOT = "@"; |
| 15 | + private static final String CRATE = "O"; |
| 16 | + private static final String BIG_CRATE_LEFT = "["; |
| 17 | + private static final String BIG_CRATE_RIGHT = "]"; |
| 18 | + private static final String WALL = "#"; |
| 19 | + private static final String FREE = "."; |
| 20 | + |
6 | 21 | public Day15() {
|
7 | 22 | super(2024, 15, "Warehouse Woes");
|
8 | 23 | }
|
9 | 24 |
|
| 25 | + private Pair<Grid<String>, String[]> loadInput() { |
| 26 | + final List<String> raw = getInputLines(); |
| 27 | + final int blankIdx = raw.indexOf(""); |
| 28 | + |
| 29 | + return Pair.of( |
| 30 | + Grid.create( |
| 31 | + raw.subList(0, blankIdx).stream(), |
| 32 | + str -> new ArrayList<>(Arrays.stream(str.split("")).toList()) |
| 33 | + ).moveCellToFirstOccurrence(ROBOT), |
| 34 | + String.join("", raw.subList(blankIdx + 1, raw.size())).split("") |
| 35 | + ); |
| 36 | + } |
| 37 | + |
| 38 | + private Pair<Grid<String>, String[]> loadResizedInput() { |
| 39 | + var initial = loadInput(); |
| 40 | + |
| 41 | + Grid<String> resizedGrid = Grid.emptyGrid(initial.first().getRows(), initial.first().getCols() * 2, () -> "."); |
| 42 | + for (int row = 0; row < resizedGrid.getRows(); row++) { |
| 43 | + for (int initialCol = 0, col = 0; initialCol < initial.first().getCols() && col < resizedGrid.getCols(); initialCol++, col += 2) { |
| 44 | + final String initialValue = initial.first().get(row, initialCol); |
| 45 | + switch (initialValue) { |
| 46 | + case WALL -> { |
| 47 | + resizedGrid.set(row, col, WALL); |
| 48 | + resizedGrid.set(row, col + 1, WALL); |
| 49 | + } |
| 50 | + case FREE -> { |
| 51 | + resizedGrid.set(row, col, FREE); |
| 52 | + resizedGrid.set(row, col + 1, FREE); |
| 53 | + } |
| 54 | + case CRATE -> { |
| 55 | + resizedGrid.set(row, col, BIG_CRATE_LEFT); |
| 56 | + resizedGrid.set(row, col + 1, BIG_CRATE_RIGHT); |
| 57 | + } |
| 58 | + case ROBOT -> { |
| 59 | + resizedGrid.set(row, col, ROBOT); |
| 60 | + resizedGrid.set(row, col + 1, FREE); |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + return Pair.of(resizedGrid.moveCellToFirstOccurrence(ROBOT), initial.second()); |
| 67 | + } |
| 68 | + |
| 69 | + private boolean canPush(Grid<String> grid, Direction direction, boolean part2) { |
| 70 | + int steps = 0; |
| 71 | + boolean canPush = false; |
| 72 | + |
| 73 | + while (grid.peek(direction).equals(CRATE)) { |
| 74 | + grid.move(direction); |
| 75 | + steps++; |
| 76 | + } |
| 77 | + |
| 78 | + if (grid.peek(direction).equals(FREE)) { |
| 79 | + canPush = true; |
| 80 | + } |
| 81 | + |
| 82 | + grid.move(direction.getOpposite(), steps); |
| 83 | + return canPush; |
| 84 | + } |
| 85 | + |
| 86 | + private void pushCrates(Grid<String> grid, Direction direction, boolean part2) { |
| 87 | + int steps = 0; |
| 88 | + |
| 89 | + while (grid.peek(direction).equals(CRATE)) { |
| 90 | + grid.move(direction); |
| 91 | + steps++; |
| 92 | + } |
| 93 | + |
| 94 | + for (int i = 0; i < steps; i++) { |
| 95 | + grid.set(direction, CRATE); |
| 96 | + grid.set(FREE); |
| 97 | + grid.move(direction.getOpposite()); |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + private void doMove(Grid<String> grid, Direction direction, boolean part2) { |
| 102 | + switch (grid.peek(direction)) { |
| 103 | + case WALL -> { |
| 104 | + } |
| 105 | + case FREE -> { |
| 106 | + grid.set(direction, ROBOT); |
| 107 | + grid.set(FREE); |
| 108 | + grid.move(direction); |
| 109 | + } |
| 110 | + case CRATE, BIG_CRATE_LEFT, BIG_CRATE_RIGHT -> { |
| 111 | + if (canPush(grid, direction, part2)) { |
| 112 | + pushCrates(grid, direction, part2); |
| 113 | + grid.set(direction, ROBOT); |
| 114 | + grid.set(FREE); |
| 115 | + grid.move(direction); |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + private int sumCrateGpsCoords(Grid<String> grid) { |
| 122 | + AtomicInteger sum = new AtomicInteger(); |
| 123 | + grid.forEach(ctx -> { |
| 124 | + if (ctx.value().equals(CRATE)) { |
| 125 | + sum.addAndGet((100 * ctx.position().first()) + (ctx.position().second())); |
| 126 | + } |
| 127 | + }); |
| 128 | + return sum.get(); |
| 129 | + } |
| 130 | + |
| 131 | + private List<Direction> translateInstructions(String[] instructions) { |
| 132 | + return Arrays.stream(instructions).map(inst -> switch(inst) { |
| 133 | + case "^": |
| 134 | + yield Direction.NORTH; |
| 135 | + case ">": |
| 136 | + yield Direction.EAST; |
| 137 | + case "v": |
| 138 | + yield Direction.SOUTH; |
| 139 | + case "<": |
| 140 | + yield Direction.WEST; |
| 141 | + default: |
| 142 | + throw new IllegalStateException("Unexpected instruction: " + inst); |
| 143 | + }).toList(); |
| 144 | + } |
| 145 | + |
10 | 146 | @Override
|
11 | 147 | public String partOne() {
|
12 |
| - getInputLines().forEach(System.out::println); |
13 |
| - return ""; |
| 148 | + final var input = loadInput(); |
| 149 | + |
| 150 | + final var grid = input.first(); |
| 151 | + translateInstructions(input.second()).forEach(inst -> doMove(grid, inst, false)); |
| 152 | + |
| 153 | + return String.valueOf(sumCrateGpsCoords(grid)); |
14 | 154 | }
|
15 | 155 |
|
16 | 156 | @Override
|
17 | 157 | public String partTwo() {
|
| 158 | + final var input = loadResizedInput(); |
| 159 | + |
| 160 | + final var grid = input.first(); |
| 161 | + translateInstructions(input.second()).forEach(inst -> doMove(grid, inst, true)); |
| 162 | + |
18 | 163 | return "";
|
19 | 164 | }
|
20 | 165 | }
|
0 commit comments