5
5
import java .util .regex .Matcher ;
6
6
import java .util .regex .Pattern ;
7
7
import java .util .stream .Collectors ;
8
+ import java .util .stream .IntStream ;
9
+ import java .util .stream .Stream ;
8
10
9
11
public class Day11 extends Day2016 {
10
12
private static final Pattern ITEM_PATTERN = Pattern .compile ("(\\ w+)(?:-compatible)? (microchip|generator)" );
@@ -17,76 +19,82 @@ public static void main(String[] args) {
17
19
new Day11 ().printParts ();
18
20
}
19
21
20
- private record State (int elevator , List <Set <String >> floors ) {
22
+ private record ElementPosition (int generatorFloor , int microchipFloor ) implements Comparable <ElementPosition > {
23
+ @ Override
24
+ public int compareTo (ElementPosition o ) {
25
+ int cmp = Integer .compare (generatorFloor , o .generatorFloor );
26
+ if (cmp != 0 ) return cmp ;
27
+ return Integer .compare (microchipFloor , o .microchipFloor );
28
+ }
29
+ }
30
+
31
+ private record State (int elevatorFloor , List <ElementPosition > elements ) {
32
+ public State {
33
+ elements = new ArrayList <>(elements );
34
+ Collections .sort (elements );
35
+ }
36
+
21
37
public boolean isValid () {
22
- for (Set <String > floor : floors ) {
23
- Set <String > generators = floor .stream ()
24
- .filter (s -> s .endsWith ("G" ))
25
- .collect (Collectors .toSet ());
26
- Set <String > microchips = floor .stream ()
27
- .filter (s -> s .endsWith ("M" ))
28
- .collect (Collectors .toSet ());
29
-
30
- if (!generators .isEmpty ()) {
31
- for (String chip : microchips ) {
32
- String gen = chip .substring (0 , chip .length () - 1 ) + "G" ;
33
- if (!generators .contains (gen )) {
34
- return false ;
35
- }
38
+ for (ElementPosition elem : elements ) {
39
+ if (elem .microchipFloor != elem .generatorFloor ) {
40
+ final int floor = elem .microchipFloor ;
41
+ boolean hasGenerator = elements .stream ()
42
+ .anyMatch (e -> e .generatorFloor == floor );
43
+ if (hasGenerator ) {
44
+ return false ;
36
45
}
37
46
}
38
47
}
39
48
return true ;
40
49
}
41
50
42
51
public boolean isComplete () {
43
- return floors .get (0 ).isEmpty () &&
44
- floors .get (1 ).isEmpty () &&
45
- floors .get (2 ).isEmpty ();
52
+ return elements .stream ()
53
+ .allMatch (e -> e .generatorFloor == 3 && e .microchipFloor == 3 );
46
54
}
47
55
48
56
public List <State > nextStates () {
49
57
List <State > states = new ArrayList <>();
50
- Set <String > currentFloor = floors .get (elevator );
51
-
52
- // Try moving one or two items
53
- List <List <String >> itemCombos = new ArrayList <>();
54
- currentFloor .forEach (item -> itemCombos .add (List .of (item )));
55
- for (String item1 : currentFloor ) {
56
- for (String item2 : currentFloor ) {
57
- if (item1 .compareTo (item2 ) < 0 ) {
58
- itemCombos .add (List .of (item1 , item2 ));
59
- }
58
+ List <MoveItem > movable = new ArrayList <>();
59
+
60
+ for (int i = 0 ; i < elements .size (); i ++) {
61
+ ElementPosition elem = elements .get (i );
62
+ if (elem .generatorFloor == elevatorFloor ) {
63
+ movable .add (new MoveItem (i , true ));
64
+ }
65
+ if (elem .microchipFloor == elevatorFloor ) {
66
+ movable .add (new MoveItem (i , false ));
60
67
}
61
68
}
62
69
63
- // Try moving up or down
64
- for (int newElevator : List .of (elevator - 1 , elevator + 1 )) {
65
- if (newElevator >= 0 && newElevator < floors .size ()) {
66
- for (List <String > items : itemCombos ) {
67
- List <Set <String >> newFloors = new ArrayList <>();
68
- for (int i = 0 ; i < floors .size (); i ++) {
69
- newFloors .add (new HashSet <>(floors .get (i )));
70
+ for (int dir : new int []{-1 , 1 }) {
71
+ int newFloor = elevatorFloor + dir ;
72
+ if (newFloor < 0 || newFloor >= 4 ) continue ;
73
+
74
+ for (int count = 1 ; count <= Math .min (2 , movable .size ()); count ++) {
75
+ Combinations .combinations (movable , count ).forEach (combo -> {
76
+ List <ElementPosition > newElements = new ArrayList <>(elements );
77
+ for (MoveItem item : combo ) {
78
+ ElementPosition e = newElements .get (item .elementIndex );
79
+ if (item .isGenerator ) {
80
+ newElements .set (item .elementIndex , new ElementPosition (newFloor , e .microchipFloor ));
81
+ } else {
82
+ newElements .set (item .elementIndex , new ElementPosition (e .generatorFloor , newFloor ));
83
+ }
70
84
}
71
-
72
- // Move items
73
- items .forEach (item -> {
74
- newFloors .get (elevator ).remove (item );
75
- newFloors .get (newElevator ).add (item );
76
- });
77
-
78
- State newState = new State (newElevator , newFloors );
85
+ State newState = new State (newFloor , newElements );
79
86
if (newState .isValid ()) {
80
87
states .add (newState );
81
88
}
82
- }
89
+ });
83
90
}
84
91
}
85
-
86
92
return states ;
87
93
}
88
94
}
89
95
96
+ private record MoveItem (int elementIndex , boolean isGenerator ) {}
97
+
90
98
private int findMinSteps (State initial ) {
91
99
Queue <State > queue = new ArrayDeque <>();
92
100
Map <State , Integer > visited = new HashMap <>();
@@ -103,38 +111,52 @@ private int findMinSteps(State initial) {
103
111
104
112
for (State next : current .nextStates ()) {
105
113
if (!visited .containsKey (next )) {
106
- queue .add (next );
107
114
visited .put (next , steps + 1 );
115
+ queue .add (next );
108
116
}
109
117
}
110
118
}
111
-
112
119
return -1 ;
113
120
}
114
121
115
- private State parseInput (boolean includePart2Items ) {
116
- List <Set <String >> floors = new ArrayList <>();
117
- for (int i = 0 ; i < 4 ; i ++) {
118
- floors .add (new HashSet <>());
119
- }
120
-
122
+ private State parseInput (boolean includePart2 ) {
123
+ Map <String , Integer > generatorFloors = new HashMap <>();
124
+ Map <String , Integer > microchipFloors = new HashMap <>();
125
+
121
126
int floor = 0 ;
122
127
for (String line : dayStream ().toList ()) {
123
- Matcher matcher = ITEM_PATTERN .matcher (line );
124
- while (matcher .find ()) {
125
- String element = matcher .group (1 );
126
- String type = matcher .group (2 );
127
- floors .get (floor ).add (element .substring (0 , 2 ).toUpperCase () +
128
- (type .equals ("generator" ) ? "G" : "M" ));
128
+ Matcher m = ITEM_PATTERN .matcher (line );
129
+ while (m .find ()) {
130
+ String element = m .group (1 ).substring (0 , 2 ).toUpperCase ();
131
+ String type = m .group (2 );
132
+ if (type .equals ("generator" )) {
133
+ generatorFloors .put (element , floor );
134
+ } else {
135
+ microchipFloors .put (element , floor );
136
+ }
129
137
}
130
138
floor ++;
131
139
}
132
140
133
- if (includePart2Items ) {
134
- floors .get (0 ).addAll (Set .of ("ELG" , "ELM" , "DIG" , "DIM" ));
141
+ if (includePart2 ) {
142
+ generatorFloors .put ("EL" , 0 );
143
+ microchipFloors .put ("EL" , 0 );
144
+ generatorFloors .put ("DI" , 0 );
145
+ microchipFloors .put ("DI" , 0 );
135
146
}
136
147
137
- return new State (0 , floors );
148
+ Set <String > allElements = new HashSet <>();
149
+ allElements .addAll (generatorFloors .keySet ());
150
+ allElements .addAll (microchipFloors .keySet ());
151
+
152
+ List <ElementPosition > elements = allElements .stream ()
153
+ .map (e -> new ElementPosition (
154
+ generatorFloors .getOrDefault (e , -1 ),
155
+ microchipFloors .getOrDefault (e , -1 )
156
+ ))
157
+ .toList ();
158
+
159
+ return new State (0 , elements );
138
160
}
139
161
140
162
@ Override
@@ -147,3 +169,22 @@ public Object part2() {
147
169
return findMinSteps (parseInput (true ));
148
170
}
149
171
}
172
+
173
+ class Combinations {
174
+ static <T > Stream <List <T >> combinations (List <T > items , int k ) {
175
+ if (k == 0 ) {
176
+ return Stream .of (Collections .emptyList ());
177
+ } else {
178
+ return IntStream .range (0 , items .size ()).boxed ()
179
+ .flatMap (i -> combinations (items .subList (i +1 , items .size ()), k -1 )
180
+ .map (t -> prepend (items .get (i ), t )));
181
+ }
182
+ }
183
+
184
+ private static <T > List <T > prepend (T head , List <T > tail ) {
185
+ List <T > result = new ArrayList <>();
186
+ result .add (head );
187
+ result .addAll (tail );
188
+ return result ;
189
+ }
190
+ }
0 commit comments