1
+ package aoc2021 ;
2
+
3
+ import java .io .IOException ;
4
+ import java .util .ArrayList ;
5
+ import java .util .Collections ;
6
+ import java .util .List ;
7
+ import java .util .Map ;
8
+ import java .util .OptionalInt ;
9
+ import java .util .concurrent .ConcurrentHashMap ;
10
+ import java .util .stream .IntStream ;
11
+ import java .util .stream .Stream ;
12
+ import static java .lang .Math .max ;
13
+ import static java .lang .Math .min ;
14
+
15
+ /**
16
+ * Advent of Code (AOC) 2021 Day 23 Amphipod
17
+ */
18
+ public class D23 {
19
+
20
+ record State (List <List <String >> rooms , List <String > hallway ) {
21
+ int roomSize () {
22
+ return amphipods ().mapToInt (a -> a .charAt (1 ) - '0' ).max ().getAsInt ();
23
+ }
24
+ Stream <String > amphipodsInRooms () {
25
+ return rooms .stream ().flatMap (r -> r .stream ()).filter (a -> a .length () > 1 );
26
+ }
27
+ Stream <String > amphipods () {
28
+ return Stream .concat (hallway .stream ().filter (a -> a .length () > 1 ), amphipodsInRooms ());
29
+ }
30
+ }
31
+
32
+ record Move (State state , int energy ){}
33
+
34
+ public static void main (String ... args ) throws IOException {
35
+ State state1 = new State (List .of (List .of ("D1" ,"C1" ), List .of ("C2" , "A1" ), List .of ("D2" ,"A2" ), List .of ("B1" ,"B2" )), Collections .nCopies (11 , "." ));
36
+ State state2 = new State (List .of (List .of ("D1" ,"D3" ,"D4" ,"C1" ), List .of ("C2" ,"C3" ,"B3" , "A1" ), List .of ("D2" ,"B4" ,"A3" ,"A2" ), List .of ("B1" ,"A4" ,"C4" ,"B2" )), Collections .nCopies (11 , "." ));
37
+
38
+ int part1 = solve (state1 );
39
+ System .out .printf ("part 1: %s\n " , part1 );
40
+
41
+ int part2 = solve (state2 ); // will take some time
42
+ System .out .printf ("part 2: %s\n " , part2 );
43
+ }
44
+
45
+ static int solve (State state ) {
46
+ Map <State ,Integer > energy = new ConcurrentHashMap <>(Map .of (state , 0 )); // States and lowest energy to achieve the state
47
+ int min = Integer .MAX_VALUE ;
48
+ for (int updated = 0 ; energy .size () > updated ;) { //while no more updates
49
+ updated = energy .size ();
50
+ // get all the possible moves from current states forward
51
+ var possible = energy .entrySet ().stream ().flatMap (e -> possibleMoves (e .getKey (), e .getValue ()).stream ()).toList ();
52
+ possible .forEach (m -> energy .merge (m .state , m .energy , (a ,b ) -> min (a ,b ))); // merge new minimum energy values
53
+ // check if we have finish moves and get minimum energy for those
54
+ int possinleMin = possible .stream ().filter (m -> finished (m .state )).mapToInt (m -> m .energy ).min ().orElse (Integer .MAX_VALUE );
55
+ min = min (min , possinleMin );
56
+ }
57
+ return min ;
58
+ }
59
+
60
+ static boolean finished (State state ) { // All rooms have correct entries inside
61
+ return IntStream .range (0 , 4 ).mapToObj (i -> state .rooms .get (i ).stream ().allMatch (r -> r .charAt (0 ) == 'A' + i )).allMatch (b -> b == true );
62
+ }
63
+
64
+ static List <Move > possibleMoves (State state , int energy ) {
65
+ return state .amphipods ().flatMap (amph -> possibleMoves (amph , state , energy ).stream ()).toList ();
66
+ }
67
+
68
+ static List <Move > possibleMoves (String amphipod , State state , int energy ) { // all the possible moves for single amphipod
69
+ List <Move > moves = new ArrayList <>();
70
+ moves .addAll (hallwayToRoom (amphipod , state , energy ));
71
+ moves .addAll (roomToHallway (amphipod , state , energy ));
72
+ return moves ;
73
+ }
74
+
75
+ static List <Move > roomToHallway (String amph , State state , int energy ) {
76
+ OptionalInt currentRoom = IntStream .range (0 , 4 ).filter (i -> state .rooms .get (i ).contains (amph )).findAny ();
77
+ if (finished (amph , state ) || !currentRoom .isPresent ()) {
78
+ return List .of ();
79
+ }
80
+ List <String > room = state .rooms .get (currentRoom .getAsInt ());
81
+ if (!room .stream ().dropWhile (p -> p .equals ("." )).findFirst ().get ().equals (amph )) { // first in room to m ove
82
+ return List .of ();
83
+ }
84
+ int toHallwaySteps = room .indexOf (amph ) + 1 ;
85
+ int hallIndex = roomHallIndex (currentRoom .getAsInt ());
86
+
87
+ List <Move > res = new ArrayList <>();
88
+ for (int i = 0 ; i < 11 ; i ++) { // iterate through hall
89
+ if (!roomToHallway .contains (i ) && clearPath (amph , i , hallIndex , state )) { // ignore doorways and check for clear path
90
+ List <String > r = state .rooms .get (currentRoom .getAsInt ());
91
+ var nroom = replace (r , r .indexOf (amph ), "." );
92
+ var rooms = replace (state .rooms , currentRoom .getAsInt (), nroom );
93
+ var hall = replace (state .hallway , i , amph );
94
+ State s = new State (rooms , hall ); // new state
95
+ res .add (new Move (s , energy + energy (amph , toHallwaySteps +Math .abs (hallIndex -i ))));
96
+ }
97
+
98
+ }
99
+ return res ;
100
+ }
101
+
102
+ static List <Move > hallwayToRoom (String amph , State state , int energy ) {
103
+ int amphIdx = state .hallway .indexOf (amph );
104
+ if (amphIdx != -1 ) { // amphipod is in hallway
105
+ List <String > room = targetRoom (amph , state );
106
+ int hallRoomIdx = targetRoomHallIndex (amph , state );
107
+ if (acceptIntoRoom (room , amph ) && clearPathToRoom (amph , state )) { // it can move to room
108
+ int roomIdx = IntStream .range (0 , room .size ()).filter (i -> !room .get (i ).equals ("." )).findFirst ().orElse (room .size ())-1 ;
109
+ var newRoom = replace (room , roomIdx , amph );
110
+ var rooms = replace (state .rooms , targetRoomIndex (amph ), newRoom );
111
+ var hall = replace (state .hallway , amphIdx , "." );
112
+ int steps = max (hallRoomIdx , amphIdx ) - min (hallRoomIdx , amphIdx );
113
+ Move m = new Move (new State (rooms , hall ), energy + energy (amph , steps + roomIdx + 1 ));
114
+ return List .of (m );
115
+ }
116
+ }
117
+ return List .of ();
118
+ }
119
+
120
+ static Map <Character ,Integer > energy = Map .of ('A' ,1 , 'B' , 10 , 'C' , 100 , 'D' , 1000 );
121
+
122
+ static int energy (String amph , int steps ) {
123
+ return energy .get (amph .charAt (0 )) * steps ;
124
+ }
125
+
126
+ static boolean acceptIntoRoom (List <String > room , String amphipod ) {
127
+ return !room .stream ().anyMatch (a -> !(a .startsWith (amphipod .substring (0 , 1 )) || a .equals ("." )));
128
+ }
129
+
130
+ static int targetRoomHallIndex (String amphipod , State state ) {
131
+ return roomToHallway .get (amphipod .charAt (0 )-'A' );
132
+ }
133
+
134
+ static List <Integer > roomToHallway = List .of (2 , 4 , 6 , 8 );
135
+ static int roomHallIndex (int roomIndex ) {
136
+ return roomToHallway .get (roomIndex );
137
+ }
138
+
139
+ static List <String > targetRoom (String amph , State state ) {
140
+ return state .rooms .get (targetRoomIndex (amph ));
141
+ }
142
+
143
+ static int targetRoomIndex (String amph ) {
144
+ return amph .charAt (0 ) - 'A' ;
145
+ }
146
+
147
+ static boolean finished (String amph , State state ) {
148
+ List <String > room = targetRoom (amph , state );
149
+ if (!room .contains (amph )) {
150
+ return false ;
151
+ }
152
+ return !room .stream ()
153
+ .dropWhile (a -> !a .equals (amph ))
154
+ .anyMatch (a -> !a .startsWith (amph .substring (0 ,1 )));
155
+ }
156
+
157
+ static boolean clearPath (String amph , int idx1 , int idx2 , State state ) {
158
+ List <String > path = state .hallway .subList (min (idx1 , idx2 ), max (idx1 , idx2 )+1 );
159
+ return !path .stream ().anyMatch (h -> !h .equals (amph ) && h .length () > 1 );
160
+ }
161
+
162
+ static boolean clearPathToRoom (String amph , State state ) {
163
+ return clearPath (amph , targetRoomHallIndex (amph , state ), state .hallway .indexOf (amph ), state );
164
+ }
165
+
166
+ static boolean sameTypeInLast (char amphipod , String room ) {
167
+ return Character .toUpperCase (room .charAt (1 )) == Character .toUpperCase (amphipod );
168
+ }
169
+
170
+ static <T > List <T > replace (List <T > list , int index , T value ) {
171
+ List <T > l = new ArrayList <>(list );
172
+ l .set (index , value );
173
+ return l ;
174
+ }
175
+ }
0 commit comments