-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathDay19.java
129 lines (110 loc) · 4.12 KB
/
Day19.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.sbaars.adventofcode.year23.days;
import com.sbaars.adventofcode.year23.Day2023;
import java.util.*;
public class Day19 extends Day2023 {
public Day19() {
super(19);
}
public static void main(String[] args) {
new Day19().printParts();
}
record Rule(char category, char operator, long value, String target) {
static Rule parse(String s) {
if (!s.contains(":")) return new Rule(' ', ' ', 0, s);
var condition = s.split(":");
return new Rule(
condition[0].charAt(0),
condition[0].charAt(1),
Long.parseLong(condition[0].substring(2)),
condition[1]
);
}
}
record Workflow(List<Rule> rules) {
static Workflow parse(String s) {
var parts = s.substring(s.indexOf('{')+1, s.length()-1).split(",");
return new Workflow(Arrays.stream(parts).map(Rule::parse).toList());
}
}
record Part(Map<Character, Long> ratings) {
static Part parse(String s) {
var ratings = new HashMap<Character, Long>();
var parts = s.substring(1, s.length()-1).split(",");
for (var part : parts) {
ratings.put(part.charAt(0), Long.parseLong(part.substring(2)));
}
return new Part(ratings);
}
}
record Range(long min, long max) {
long size() { return max - min + 1; }
Range intersect(long newMin, long newMax) {
return new Range(Math.max(min, newMin), Math.min(max, newMax));
}
}
@Override
public Object part1() {
var parts = day().split("\n\n");
var workflows = parseWorkflows(parts[0]);
return Arrays.stream(parts[1].split("\n"))
.map(Part::parse)
.filter(p -> process(p, workflows, "in"))
.mapToLong(p -> p.ratings.values().stream().mapToLong(Long::longValue).sum())
.sum();
}
private Map<String, Workflow> parseWorkflows(String input) {
var workflows = new HashMap<String, Workflow>();
for (var line : input.split("\n")) {
var parts = line.split("\\{", 2);
workflows.put(parts[0], Workflow.parse("{" + parts[1]));
}
return workflows;
}
private boolean process(Part part, Map<String, Workflow> workflows, String current) {
if (current.equals("A")) return true;
if (current.equals("R")) return false;
var workflow = workflows.get(current);
for (var rule : workflow.rules) {
if (rule.category == ' ' || evaluate(part, rule)) {
return process(part, workflows, rule.target);
}
}
throw new IllegalStateException("No matching rule found");
}
private boolean evaluate(Part part, Rule rule) {
if (rule.category == ' ') return true;
var value = part.ratings.get(rule.category);
return rule.operator == '<' ? value < rule.value : value > rule.value;
}
@Override
public Object part2() {
var workflows = parseWorkflows(day().split("\n\n")[0]);
var initial = new HashMap<Character, Range>();
"xmas".chars().forEach(c -> initial.put((char)c, new Range(1, 4000)));
return countCombinations(workflows, initial, "in");
}
private long countCombinations(Map<String, Workflow> workflows, Map<Character, Range> ranges, String current) {
if (current.equals("R")) return 0;
if (current.equals("A")) return ranges.values().stream().mapToLong(Range::size).reduce(1L, (a, b) -> a * b);
var workflow = workflows.get(current);
var result = 0L;
var currentRanges = new HashMap<>(ranges);
for (var rule : workflow.rules) {
if (rule.category == ' ') {
result += countCombinations(workflows, currentRanges, rule.target);
continue;
}
var range = currentRanges.get(rule.category);
Map<Character, Range> newRanges = new HashMap<>(currentRanges);
if (rule.operator == '<') {
newRanges.put(rule.category, range.intersect(1, rule.value - 1));
currentRanges.put(rule.category, range.intersect(rule.value, 4000));
} else {
newRanges.put(rule.category, range.intersect(rule.value + 1, 4000));
currentRanges.put(rule.category, range.intersect(1, rule.value));
}
result += countCombinations(workflows, newRanges, rule.target);
}
return result;
}
}