static void day16(String puzzleInputUri) throws IOException, InterruptedException {
final Pattern vName = Pattern.compile("([A-Z]{2})");
final Pattern vFlow = Pattern.compile("\\d+");
var valves = client.send(request.uri((URI.create(puzzleInputUri))).build(), HttpResponse.BodyHandlers.ofLines()).body()
.map(line -> {
List<String> linkedValves = new ArrayList<>();
var nameMatcher = vName.matcher(line);
nameMatcher.find();
String name = nameMatcher.group();
while (nameMatcher.find()) {
linkedValves.add(nameMatcher.group());
}
var flowMatcher = vFlow.matcher(line);
flowMatcher.find();
int flow = Integer.parseInt(flowMatcher.group());
return new Valve(name, flow, linkedValves);
})
.collect(Collectors.toMap(v -> v.name(), v -> v));
// Part 1
Set<State> states = new HashSet<>();
states.add(new State(new HashMap<>(), valves.get("AA"), null, 0));
for (int minutes = 0; minutes < 30; minutes++) {
Set<State> newStates = new HashSet<>();
for(State s : states) {
long flow = s.openValves().values().stream().mapToLong(f -> f).sum() + s.totalFlow();
if(s.player().flow() > 0 && !s.openValves().containsKey(s.player().name())) {
Map<String, Long> newOpen = new HashMap<>(s.openValves());
newOpen.put(s.player().name(), s.player().flow());
newStates.add(new State(newOpen, s.player(), null, flow));
}
s.player().linked().forEach(name -> newStates.add(new State(s.openValves(), valves.get(name), null, flow)));
}
states = newStates;
}
var answer1 = states.stream().mapToLong(State::totalFlow).max().orElseThrow();
System.out.println("Answer 1: " + answer1);
// Part 2
Set<String> useful = valves.values().stream().filter(valve -> valve.flow() > 0).map(Valve::name).collect(Collectors.toSet());
states.clear();
states.add(new State(new HashMap<>(), valves.get("AA"), valves.get("AA"), 0));
Map<Integer, Long> kpis = Map.of(5, 25L, 10, 50L, 15, 100L, 20, 140L, 25, 160L);
for(int minutes = 0; minutes < 26; minutes++) {
Set<State> newStates = new HashSet<>();
for(State s : states) {
long flow = s.openValves().values().stream().mapToLong(f -> f).sum() + s.totalFlow();
if(s.openValves().size() == useful.size()) {
newStates.add(new State(s.openValves(), valves.get("AA"), valves.get("AA"), flow));
}
int openedValves = newStates.size();
newStates.addAll(openValve(s.player(), s.elephant(), false, valves, s, flow));
newStates.addAll(openValve(s.elephant(), s.player(), false, valves, s, flow));
newStates.addAll(openValve(s.player(), s.elephant(), true, valves, s, flow));
if(openedValves == newStates.size()) {
IntStream.range(0, s.player().linked().size()).boxed()
.flatMap(i -> s.elephant().linked().stream()
.map(l -> Map.entry(s.player().linked().get(i), l))
).forEach(valvesPair -> newStates.add(
new State(s.openValves(), valves.get(valvesPair.getKey()), valves.get(valvesPair.getValue()), flow)
));
}
}
states = newStates;
if (kpis.containsKey(minutes)){
long kpi = kpis.get(minutes);
states = states.stream().filter(
s -> s.openValves().values().stream().mapToLong(f -> f).sum() >= kpi
).collect(Collectors.toSet());
}
}
var answer2 = states.stream().mapToLong(State::totalFlow).max().orElseThrow();
System.out.println("Answer 2: " + answer2);
}