Skip to content

Commit 1eee199

Browse files
committed
Day 20
1 parent a943bfd commit 1eee199

File tree

2 files changed

+240
-0
lines changed

2 files changed

+240
-0
lines changed

src/test/java/com/macasaet/Day20.java

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
package com.macasaet;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.util.*;
6+
import java.util.stream.Stream;
7+
import java.util.stream.StreamSupport;
8+
9+
import org.junit.jupiter.api.Nested;
10+
import org.junit.jupiter.api.Test;
11+
12+
/**
13+
* --- Day 20: Trench Map ---
14+
*/
15+
public class Day20 {
16+
17+
protected Stream<String> getInput() {
18+
return StreamSupport
19+
.stream(new LineSpliterator("day-20.txt"),
20+
false);
21+
}
22+
23+
public record ImageEnhancementAlgorithm(boolean[] map) {
24+
25+
public boolean isPixelLit(final int code) {
26+
return map()[code];
27+
}
28+
29+
public static ImageEnhancementAlgorithm parse(final String line) {
30+
final var map = new boolean[line.length()];
31+
for (int i = line.length(); --i >= 0; map[i] = line.charAt(i) == '#') ;
32+
return new ImageEnhancementAlgorithm(map);
33+
}
34+
}
35+
36+
protected record Coordinate(int x, int y) {
37+
}
38+
39+
public record Image(SortedMap<Integer, SortedMap<Integer, Boolean>> pixels, int minX, int maxX, int minY,
40+
int maxY, boolean isEven) {
41+
public Image enhance(final ImageEnhancementAlgorithm algorithm) {
42+
final var enhancedPixelMap = new TreeMap<Integer, SortedMap<Integer, Boolean>>();
43+
int enhancedMinX = minX;
44+
int enhancedMaxX = maxX;
45+
int enhancedMinY = minY;
46+
int enhancedMaxY = maxY;
47+
for (int i = minX - 1; i <= maxX + 1; i++) {
48+
final var targetRow = new TreeMap<Integer, Boolean>();
49+
for (int j = minY - 1; j <= maxY + 1; j++) {
50+
final int replacementId = decode(i, j, !isEven && algorithm.isPixelLit(0));
51+
final var shouldLight = algorithm.isPixelLit(replacementId);
52+
if (shouldLight) {
53+
// save space by only storing an entry when lit
54+
targetRow.put(j, true);
55+
enhancedMinY = Math.min(enhancedMinY, j);
56+
enhancedMaxY = Math.max(enhancedMaxY, j);
57+
}
58+
}
59+
if (!targetRow.isEmpty()) {
60+
// save space by only storing a row if at least one cell is lit
61+
enhancedPixelMap.put(i, Collections.unmodifiableSortedMap(targetRow));
62+
enhancedMinX = Math.min(enhancedMinX, i);
63+
enhancedMaxX = Math.max(enhancedMaxX, i);
64+
}
65+
}
66+
return new Image(Collections.unmodifiableSortedMap(enhancedPixelMap), enhancedMinX, enhancedMaxX, enhancedMinY, enhancedMaxY, !isEven);
67+
}
68+
69+
int decode(final int x, final int y, boolean voidIsLit) {
70+
final var list = getNeighbouringCoordinates(x, y).stream()
71+
.map(coordinate -> isBitSet(coordinate, voidIsLit))
72+
.toList();
73+
int result = 0;
74+
for (int i = list.size(); --i >= 0; ) {
75+
if (list.get(i)) {
76+
final int shiftDistance = list.size() - i - 1;
77+
result |= 1 << shiftDistance;
78+
}
79+
}
80+
if (result < 0 || result > 512) {
81+
throw new IllegalStateException("Unable to decode pixel at " + x + ", " + y);
82+
}
83+
return result;
84+
}
85+
86+
boolean isBitSet(final Coordinate coordinate, boolean voidIsLit) {
87+
final var row = pixels().get(coordinate.x());
88+
if((coordinate.x() < minX || coordinate.x() > maxX) && row == null) {
89+
return voidIsLit;
90+
}
91+
else if(row == null) {
92+
return false;
93+
}
94+
return row.getOrDefault(coordinate.y(), (coordinate.y() < minY || coordinate.y() > maxY) && voidIsLit);
95+
}
96+
97+
List<Coordinate> getNeighbouringCoordinates(int x, int y) {
98+
return Arrays.asList(
99+
new Coordinate(x - 1, y - 1),
100+
new Coordinate(x - 1, y),
101+
new Coordinate(x - 1, y + 1),
102+
new Coordinate(x, y - 1),
103+
new Coordinate(x, y),
104+
new Coordinate(x, y + 1),
105+
new Coordinate(x + 1, y - 1),
106+
new Coordinate(x + 1, y),
107+
new Coordinate(x + 1, y + 1)
108+
);
109+
}
110+
111+
public long countLitPixels() {
112+
return pixels().values()
113+
.stream()
114+
.flatMap(row -> row.values().stream())
115+
.filter(isLit -> isLit)
116+
.count();
117+
}
118+
119+
public int width() {
120+
return maxX - minX + 1;
121+
}
122+
123+
public int height() {
124+
return maxY - minY + 1;
125+
}
126+
127+
public String toString() {
128+
final var builder = new StringBuilder();
129+
builder.append(width()).append('x').append(height()).append('\n');
130+
for (int i = minX; i <= maxX; i++) {
131+
final var row = pixels.getOrDefault(i, Collections.emptySortedMap());
132+
for (int j = minY; j <= maxY; j++) {
133+
final var value = row.getOrDefault(j, false);
134+
builder.append(value ? '#' : '.');
135+
}
136+
builder.append('\n');
137+
}
138+
return builder.toString();
139+
}
140+
141+
public static Image parse(final List<String> lines) {
142+
final var pixels = new TreeMap<Integer, SortedMap<Integer, Boolean>>();
143+
final int minX = 0;
144+
final int minY = 0;
145+
int maxX = 0;
146+
int maxY = 0;
147+
for (int i = lines.size(); --i >= 0; ) {
148+
final var line = lines.get(i);
149+
final var row = new TreeMap<Integer, Boolean>();
150+
for (int j = line.length(); --j >= 0; ) {
151+
final var pixel = line.charAt(j);
152+
row.put(j, pixel == '#');
153+
if (pixel == '#') {
154+
row.put(j, true);
155+
maxY = Math.max(maxY, j);
156+
}
157+
}
158+
if (!row.isEmpty()) {
159+
maxX = Math.max(maxX, i);
160+
pixels.put(i, Collections.unmodifiableSortedMap(row));
161+
}
162+
}
163+
return new Image(Collections.unmodifiableSortedMap(pixels), minX, maxX, minY, maxY, true);
164+
}
165+
166+
}
167+
168+
@Nested
169+
public class ImageTest {
170+
@Test
171+
public final void testToInt() {
172+
final var string = """
173+
#..#.
174+
#....
175+
##..#
176+
..#..
177+
..###
178+
""";
179+
final var image = Image.parse(Arrays.asList(string.split("\n")));
180+
assertEquals(34, image.decode(2, 2, false));
181+
}
182+
183+
@Test
184+
public final void flipAllOn() {
185+
final var template = "#........";
186+
final var imageString = """
187+
...
188+
...
189+
...
190+
""";
191+
final var image = Image.parse(Arrays.asList(imageString.split("\n")));
192+
final var result = image.enhance(ImageEnhancementAlgorithm.parse(template));
193+
assertTrue(result.pixels().get(1).get(1));
194+
}
195+
196+
@Test
197+
public final void turnOffPixel() {
198+
final var templateBuilder = new StringBuilder();
199+
for (int i = 511; --i >= 0; templateBuilder.append('#')) ;
200+
templateBuilder.append('.');
201+
final var template = templateBuilder.toString();
202+
final var imageString = """
203+
###
204+
###
205+
###
206+
""";
207+
final var image = Image.parse(Arrays.asList(imageString.split("\n")));
208+
final var result = image.enhance(ImageEnhancementAlgorithm.parse(template));
209+
final var middleRow = result.pixels().get(1);
210+
assertFalse(middleRow.containsKey(1));
211+
}
212+
}
213+
214+
@Test
215+
public final void part1() {
216+
final var list = getInput().toList();
217+
final var algorithm = ImageEnhancementAlgorithm.parse(list.get(0));
218+
final var image = Image.parse(list.subList(2, list.size()))
219+
.enhance(algorithm)
220+
.enhance(algorithm);
221+
System.out.println("Part 1: " + image.countLitPixels());
222+
}
223+
224+
@Test
225+
public final void part2() {
226+
final var list = getInput().toList();
227+
final var algorithm = ImageEnhancementAlgorithm.parse(list.get(0));
228+
var image = Image.parse(list.subList(2, list.size()));
229+
for(int _i = 50; --_i >= 0; image = image.enhance(algorithm));
230+
System.out.println("Part 2: " + image.countLitPixels());
231+
}
232+
233+
}

src/test/resources/sample/day-20.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
2+
3+
#..#.
4+
#....
5+
##..#
6+
..#..
7+
..###

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy