1
+ package com .macasaet ;
2
+
3
+ import org .junit .jupiter .api .Test ;
4
+
5
+ import java .util .Arrays ;
6
+ import java .util .Collection ;
7
+ import java .util .Collections ;
8
+ import java .util .HashMap ;
9
+ import java .util .List ;
10
+ import java .util .Map ;
11
+ import java .util .stream .StreamSupport ;
12
+
13
+ /**
14
+ * --- Day 14: Regolith Reservoir ---
15
+ * <a href="https://adventofcode.com/2022/day/14">https://adventofcode.com/2022/day/14</a>
16
+ */
17
+ public class Day14 {
18
+
19
+ public enum Cell {
20
+ ROCK ,
21
+ SAND
22
+ }
23
+
24
+ record Coordinate (int verticalDepth , int horizontalOffset ) {
25
+
26
+ public static Coordinate parse (final String string ) {
27
+ final var components = string .split ("," );
28
+ final var verticalDepth = Integer .parseInt (components [1 ]);
29
+ final var horizontalOffset = Integer .parseInt (components [0 ]);
30
+ return new Coordinate (verticalDepth , horizontalOffset );
31
+ }
32
+ }
33
+
34
+ public record Cave (Map <Integer , Map <Integer , Cell >> grid , int maxDepth , int minHorizontalOffset , int maxHorizontalOffset ) {
35
+
36
+ public int pourSandIntoAbyss () {
37
+ int settledSand = 0 ;
38
+ while (true ) {
39
+ var fallingSandCoordinate = new Coordinate (0 , 500 );
40
+ while (true ) {
41
+ final var next = getNextCoordinate (fallingSandCoordinate , null );
42
+ if (next != null ) {
43
+ fallingSandCoordinate = next ;
44
+ if (fallingSandCoordinate .verticalDepth () >= maxDepth ()) {
45
+ return settledSand ;
46
+ }
47
+ } else {
48
+ final var row = grid .computeIfAbsent (fallingSandCoordinate .verticalDepth (), key -> new HashMap <>());
49
+ row .put (fallingSandCoordinate .horizontalOffset (), Cell .SAND );
50
+ settledSand ++;
51
+ break ;
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ public int fillAperture () {
58
+ int settledSand = 0 ;
59
+ while (true ) {
60
+ var fallingSandCoordinate = new Coordinate (0 , 500 );
61
+ while (true ) {
62
+ final var next = getNextCoordinate (fallingSandCoordinate , floorDepth ());
63
+ if (next != null ) {
64
+ fallingSandCoordinate = next ;
65
+ } else {
66
+ final var secondRow = grid ().computeIfAbsent (1 , key -> new HashMap <>());
67
+ if (secondRow .containsKey (499 ) && secondRow .containsKey (500 ) && secondRow .containsKey (501 )) {
68
+ return settledSand + 1 ;
69
+ }
70
+ final var row = grid .computeIfAbsent (fallingSandCoordinate .verticalDepth (), key -> new HashMap <>());
71
+ row .put (fallingSandCoordinate .horizontalOffset (), Cell .SAND );
72
+ settledSand ++;
73
+ break ;
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ int floorDepth () {
80
+ return maxDepth () + 2 ;
81
+ }
82
+
83
+ Coordinate getNextCoordinate (final Coordinate start , Integer floorDepth ) {
84
+ final var x = start .verticalDepth ();
85
+ final var y = start .horizontalOffset ();
86
+ if (floorDepth != null && x + 1 >= floorDepth ) {
87
+ return null ;
88
+ }
89
+ final var nextRow = grid ().computeIfAbsent (x + 1 , key -> new HashMap <>());
90
+ if (!nextRow .containsKey (y )) {
91
+ return new Coordinate (x + 1 , y );
92
+ } else if (!nextRow .containsKey (y - 1 )) {
93
+ return new Coordinate (x + 1 , y - 1 );
94
+ } else if (!nextRow .containsKey (y + 1 )) {
95
+ return new Coordinate (x + 1 , y + 1 );
96
+ }
97
+ return null ;
98
+ }
99
+
100
+ public static Cave parse (final Collection <? extends String > lines ) {
101
+ int maxDepth = 0 ;
102
+ int maxHorizontalOffset = Integer .MIN_VALUE ;
103
+ int minHorizontalOffset = Integer .MAX_VALUE ;
104
+
105
+ final var grid = new HashMap <Integer , Map <Integer , Cell >>();
106
+ for (final var line : lines ) {
107
+ final var rockPath = parseRockPaths (line );
108
+ var last = rockPath .get (0 );
109
+ if (last .verticalDepth () > maxDepth ) {
110
+ maxDepth = last .verticalDepth ();
111
+ }
112
+ if (last .horizontalOffset () < minHorizontalOffset ) {
113
+ minHorizontalOffset = last .horizontalOffset ();
114
+ }
115
+ if (last .horizontalOffset () > maxHorizontalOffset ) {
116
+ maxHorizontalOffset = last .horizontalOffset ();
117
+ }
118
+ for (int i = 1 ; i < rockPath .size (); i ++) {
119
+ final var current = rockPath .get (i );
120
+ if (last .verticalDepth () == current .verticalDepth ()) {
121
+ // horizontal line
122
+ int start ;
123
+ int end ;
124
+ if (last .horizontalOffset () < current .horizontalOffset ()) {
125
+ start = last .horizontalOffset ();
126
+ end = current .horizontalOffset ();
127
+ } else {
128
+ start = current .horizontalOffset ();
129
+ end = last .horizontalOffset ();
130
+ }
131
+ final var row = grid .computeIfAbsent (last .verticalDepth (), key -> new HashMap <>());
132
+ for (int y = start ; y <= end ; y ++) {
133
+ row .put (y , Cell .ROCK );
134
+ }
135
+ } else {
136
+ if (last .horizontalOffset () != current .horizontalOffset ()) {
137
+ throw new IllegalStateException ("Line segments are not on the same vertical axis" );
138
+ }
139
+ // vertical line
140
+ int start ;
141
+ int end ;
142
+ if (last .verticalDepth () < current .verticalDepth ()) {
143
+ start = last .verticalDepth ();
144
+ end = current .verticalDepth ();
145
+ } else {
146
+ start = current .verticalDepth ();
147
+ end = last .verticalDepth ();
148
+ }
149
+ for (int x = start ; x <= end ; x ++) {
150
+ final var row = grid .computeIfAbsent (x , key -> new HashMap <>());
151
+ row .put (last .horizontalOffset (), Cell .ROCK );
152
+ }
153
+ }
154
+ if (current .verticalDepth () > maxDepth ) {
155
+ maxDepth = current .verticalDepth ();
156
+ }
157
+ if (current .horizontalOffset () < minHorizontalOffset ) {
158
+ minHorizontalOffset = current .horizontalOffset ();
159
+ }
160
+ if (current .horizontalOffset () > maxHorizontalOffset ) {
161
+ maxHorizontalOffset = current .horizontalOffset ();
162
+ }
163
+ last = current ;
164
+ }
165
+ }
166
+ return new Cave (grid , maxDepth , minHorizontalOffset , maxHorizontalOffset );
167
+ }
168
+
169
+ static List <Coordinate > parseRockPaths (final String line ) {
170
+ return Arrays .stream (line .split (" -> " )).map (Coordinate ::parse ).toList ();
171
+ }
172
+
173
+ @ Override
174
+ public String toString () {
175
+ final var buffer = new StringBuilder ();
176
+ for (int i = 0 ; i <= floorDepth (); i ++) {
177
+ buffer .append (i ).append (' ' );
178
+ final var row = grid .getOrDefault (i , Collections .emptyMap ());
179
+ for (int j = minHorizontalOffset (); j <= maxHorizontalOffset (); j ++) {
180
+ final var cell = row .get (j );
181
+ final char marker = cell == null ? ' ' : Cell .ROCK .equals (cell ) ? '#' : 'o' ;
182
+ buffer .append (marker );
183
+ }
184
+ buffer .append ('\n' );
185
+ }
186
+ return buffer .toString ();
187
+ }
188
+ }
189
+
190
+ protected Cave getInput () {
191
+ final var lines = StreamSupport .stream (new LineSpliterator ("day-14.txt" ), false )
192
+ .toList ();
193
+ return Cave .parse (lines );
194
+ }
195
+
196
+ @ Test
197
+ public final void part1 () {
198
+ final var cave = getInput ();
199
+ final var result = cave .pourSandIntoAbyss ();
200
+
201
+ System .out .println ("Part 1: " + result );
202
+ }
203
+
204
+ @ Test
205
+ public final void part2 () {
206
+ final var cave = getInput ();
207
+ final var result = cave .fillAperture ();
208
+
209
+ System .out .println ("Part 2: " + result );
210
+ }
211
+
212
+ }
0 commit comments