1+ import static com .github .pareronia .aoc .IntegerSequence .Range .range ;
2+ import static java .util .stream .Collectors .joining ;
3+ import static java .util .stream .Collectors .toMap ;
4+
5+ import java .util .ArrayDeque ;
6+ import java .util .ArrayList ;
7+ import java .util .Deque ;
8+ import java .util .List ;
9+ import java .util .Map ;
10+ import java .util .Set ;
11+
12+ import com .github .pareronia .aoc .CharGrid ;
13+ import com .github .pareronia .aoc .Grid .Cell ;
14+ import com .github .pareronia .aoc .StringOps ;
15+ import com .github .pareronia .aoc .Utils ;
16+ import com .github .pareronia .aoc .geometry .Direction ;
17+ import com .github .pareronia .aoc .solution .Sample ;
18+ import com .github .pareronia .aoc .solution .Samples ;
19+ import com .github .pareronia .aoc .solution .SolutionBase ;
20+
21+ public final class AoC2024_15
22+ extends SolutionBase <AoC2024_15 .Input , Integer , Integer > {
23+
24+ private static final char FLOOR = '.' ;
25+ private static final char WALL = '#' ;
26+ private static final char ROBOT = '@' ;
27+ private static final char BOX = 'O' ;
28+ private static final char BIG_BOX_LEFT = '[' ;
29+ private static final char BIG_BOX_RIGHT = ']' ;
30+
31+ private AoC2024_15 (final boolean debug ) {
32+ super (debug );
33+ }
34+
35+ public static AoC2024_15 create () {
36+ return new AoC2024_15 (false );
37+ }
38+
39+ public static AoC2024_15 createDebug () {
40+ return new AoC2024_15 (true );
41+ }
42+
43+ @ Override
44+ protected Input parseInput (final List <String > inputs ) {
45+ final List <List <String >> blocks = StringOps .toBlocks (inputs );
46+ final Grid grid = new Grid (CharGrid .from (blocks .get (0 )));
47+ final List <Direction > dirs = Utils .asCharacterStream (
48+ blocks .get (1 ).stream ().collect (joining ()))
49+ .map (Direction ::fromChar )
50+ .toList ();
51+ return new Input (grid , dirs );
52+ }
53+
54+ private int solve (
55+ final CharGrid grid ,
56+ final List <Direction > dirs ,
57+ final GetToMove getToMove
58+ ) {
59+ Cell robot = grid .getAllEqualTo (ROBOT ).findFirst ().orElseThrow ();
60+ for (final Direction dir : dirs ) {
61+ final List <Cell > toMove = getToMove .getToMove (grid , robot , dir );
62+ if (toMove .isEmpty ()) {
63+ continue ;
64+ }
65+ final Map <Cell , Character > vals = toMove .stream ()
66+ .collect (toMap (tm -> tm , grid ::getValue ));
67+ robot = robot .at (dir );
68+ for (final Cell cell : toMove ) {
69+ grid .setValue (cell , FLOOR );
70+ }
71+ for (final Cell cell : toMove ) {
72+ grid .setValue (cell .at (dir ), vals .get (cell ));
73+ }
74+ }
75+ return grid .findAllMatching (Set .of (BOX , BIG_BOX_LEFT )::contains )
76+ .mapToInt (cell -> cell .getRow () * 100 + cell .getCol ())
77+ .sum ();
78+ }
79+
80+ @ Override
81+ public Integer solvePart1 (final Input input ) {
82+ final GetToMove getToMove = (grid , robot , dir ) -> {
83+ final List <Cell > toMove = new ArrayList <>(List .of (robot ));
84+ final Deque <Cell > q = new ArrayDeque <>(toMove );
85+ while (!q .isEmpty ()) {
86+ final Cell cell = q .pop ();
87+ final Cell nxt = cell .at (dir );
88+ if (q .contains (nxt )) {
89+ continue ;
90+ }
91+ switch (grid .getValue (nxt )) {
92+ case WALL :
93+ return List .of ();
94+ case BOX :
95+ q .add (nxt );
96+ toMove .add (nxt );
97+ break ;
98+ }
99+ }
100+ return toMove ;
101+ };
102+
103+ return solve (input .grid .getGrid (), input .dirs , getToMove );
104+ }
105+
106+ @ Override
107+ public Integer solvePart2 (final Input input ) {
108+ final GetToMove getToMove = (grid , robot , dir ) -> {
109+ final List <Cell > toMove = new ArrayList <>(List .of (robot ));
110+ final Deque <Cell > q = new ArrayDeque <>(toMove );
111+ while (!q .isEmpty ()) {
112+ final Cell cell = q .pop ();
113+ final Cell nxt = cell .at (dir );
114+ if (q .contains (nxt )) {
115+ continue ;
116+ }
117+ switch (grid .getValue (nxt )) {
118+ case WALL :
119+ return List .of ();
120+ case BIG_BOX_LEFT :
121+ final Cell right = nxt .at (Direction .RIGHT );
122+ q .add (nxt );
123+ q .add (right );
124+ toMove .add (nxt );
125+ toMove .add (right );
126+ break ;
127+ case BIG_BOX_RIGHT :
128+ final Cell left = nxt .at (Direction .LEFT );
129+ q .add (nxt );
130+ q .add (left );
131+ toMove .add (nxt );
132+ toMove .add (left );
133+ break ;
134+ }
135+ }
136+ return toMove ;
137+ };
138+
139+ return solve (input .grid .getWideGrid (), input .dirs , getToMove );
140+ }
141+
142+ @ Override
143+ @ Samples ({
144+ @ Sample (method = "part1" , input = TEST1 , expected = "2028" ),
145+ @ Sample (method = "part1" , input = TEST2 , expected = "10092" ),
146+ @ Sample (method = "part2" , input = TEST2 , expected = "9021" ),
147+ })
148+ public void samples () {
149+ }
150+
151+ public static void main (final String [] args ) throws Exception {
152+ AoC2024_15 .create ().run ();
153+ }
154+
155+ private static final String TEST1 = """
156+ ########
157+ #..O.O.#
158+ ##@.O..#
159+ #...O..#
160+ #.#.O..#
161+ #...O..#
162+ #......#
163+ ########
164+
165+ <^^>>>vv<v>>v<<
166+ """ ;
167+ private static final String TEST2 = """
168+ ##########
169+ #..O..O.O#
170+ #......O.#
171+ #.OO..O.O#
172+ 173+ #O#..O...#
174+ #O..O..O.#
175+ #.OO.O.OO#
176+ #....O...#
177+ ##########
178+
179+ <vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
180+ vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
181+ ><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
182+ <<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
183+ ^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
184+ ^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
185+ >^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
186+ <><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
187+ ^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
188+ v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
189+ """ ;
190+
191+ private interface GetToMove {
192+ List <Cell > getToMove (CharGrid grid , Cell robot , Direction dir );
193+ }
194+
195+ record Grid (CharGrid gridIn ) {
196+ private static final Map <Character , char []> SCALE_UP = Map .of (
197+ FLOOR , new char [] { FLOOR , FLOOR },
198+ WALL , new char [] { WALL , WALL },
199+ ROBOT , new char [] { ROBOT , FLOOR },
200+ BOX , new char [] { BIG_BOX_LEFT , BIG_BOX_RIGHT }
201+ );
202+
203+ public CharGrid getGrid () {
204+ return this .gridIn .copy ();
205+ }
206+
207+ public CharGrid getWideGrid () {
208+ final char [][] chars = new char [this .gridIn .getHeight ()][];
209+ for (final int r : range (this .gridIn .getHeight ())) {
210+ final char [] row = new char [2 * this .gridIn .getWidth ()];
211+ for (final int c : range (this .gridIn .getWidth ())) {
212+ final char ch = this .gridIn .getValue (Cell .at (r , c ));
213+ row [2 * c ] = SCALE_UP .get (ch )[0 ];
214+ row [2 * c + 1 ] = SCALE_UP .get (ch )[1 ];
215+ }
216+ chars [r ] = row ;
217+ }
218+ return new CharGrid (chars );
219+ }
220+ }
221+
222+ record Input (Grid grid , List <Direction > dirs ) {}
223+ }
0 commit comments