aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOrfeas <38209077+0xfea5@users.noreply.github.com>2023-12-22 00:18:16 +0200
committerOrfeas <38209077+0xfea5@users.noreply.github.com>2025-10-28 23:20:45 +0200
commitaa89ad04dacc7281c81a37199fd67460b8482fba (patch)
tree8453453d26efd4da9474d24411ed00ff6d339698
parentday18 (diff)
downloadaoc23-main.tar.gz
aoc23-main.zip
-rw-r--r--day19/example.txt17
-rw-r--r--day19/solution.zig204
2 files changed, 221 insertions, 0 deletions
diff --git a/day19/example.txt b/day19/example.txt
new file mode 100644
index 0000000..e5b5d64
--- /dev/null
+++ b/day19/example.txt
@@ -0,0 +1,17 @@
1px{a<2006:qkq,m>2090:A,rfg}
2pv{a>1716:R,A}
3lnx{m>1548:A,A}
4rfg{s<537:gd,x>2440:R,A}
5qs{s>3448:A,lnx}
6qkq{x<1416:A,crn}
7crn{x>2662:A,R}
8in{s<1351:px,qqz}
9qqz{s>2770:qs,m<1801:hdj,R}
10gd{a>3333:R,R}
11hdj{m>838:A,pv}
12
13{x=787,m=2655,a=1222,s=2876}
14{x=1679,m=44,a=2067,s=496}
15{x=2036,m=264,a=79,s=2244}
16{x=2461,m=1339,a=466,s=291}
17{x=2127,m=1623,a=2188,s=1013}
diff --git a/day19/solution.zig b/day19/solution.zig
new file mode 100644
index 0000000..0c4b274
--- /dev/null
+++ b/day19/solution.zig
@@ -0,0 +1,204 @@
1const std = @import("std");
2const print = std.debug.print;
3const assert = std.debug.assert;
4const ArrayList = std.ArrayList;
5const HashMap = std.HashMap;
6const mem = std.mem;
7
8const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace);
9var gpa = std.heap.GeneralPurposeAllocator(.{}){};
10const allocator = gpa.allocator();
11
12const Condition = struct {
13 let: u64,
14 islt: bool,
15 thresh: u64,
16
17 pub fn check(self: @This(), val: u64) bool {
18 return if (self.islt) val < self.thresh else val > self.thresh;
19 }
20};
21
22const Rule = struct {
23 cond: ?Condition,
24 res: []const u8,
25
26 fn parseCondition(s: []const u8) Condition {
27 var splitCond = mem.tokenizeAny(u8, s, "<>");
28 const let: u64 = switch (splitCond.next().?[0]) {
29 'x' => 0,
30 'm' => 1,
31 'a' => 2,
32 's' => 3,
33 else => unreachable,
34 };
35 const islt = mem.count(u8, s, "<") != 0;
36 const thresh = std.fmt.parseInt(u64, splitCond.next().?, 10) catch unreachable;
37
38 return Condition{
39 .let = let,
40 .islt = islt,
41 .thresh = thresh,
42 };
43 }
44
45 pub fn parse(s: []const u8) Rule {
46 var splitRule = mem.tokenizeScalar(u8, s, ':');
47 const first = splitRule.next().?;
48 const second = splitRule.next();
49
50 return if (second) |resStr|
51 Rule{
52 .cond = parseCondition(first),
53 .res = resStr,
54 }
55 else
56 Rule{
57 .cond = null,
58 .res = first,
59 };
60 }
61
62 pub fn check(self: @This(), ratings: Ratings) ?[]const u8 {
63 if (self.cond) |cond| {
64 return if (cond.check(ratings[cond.let])) self.res else null;
65 }
66 return self.res;
67 }
68
69 pub fn getNext(list: RuleList, ratings: Ratings) []const u8 {
70 for (list) |rule| {
71 const res = rule.check(ratings) orelse continue;
72
73 return res;
74 }
75
76 unreachable;
77 }
78
79 pub fn rangeCombs(rules: RuleGraph, curr: []const u8, ranges: Ranges) u64 {
80 if (mem.eql(u8, curr, "A")) {
81 var res: u64 = 1;
82 for (ranges) |r| {
83 res *= r[1] - r[0] + 1;
84 }
85 return res;
86 } else if (mem.eql(u8, curr, "R")) {
87 return 0;
88 }
89
90 var ans: u64 = 0;
91 var ranges_rej: Ranges = undefined;
92 @memcpy(&ranges_rej, &ranges);
93
94 const rule_list = rules.get(curr).?;
95 for (rule_list) |rule| {
96 if (rule.cond) |cond| {
97 const thresh = cond.thresh;
98 const l = ranges[cond.let][0];
99 const r = ranges[cond.let][1];
100 var ranges_suc: Ranges = undefined;
101 @memcpy(&ranges_suc, &ranges_rej);
102
103 if (cond.islt) {
104 ranges_suc[cond.let] = nextRange(
105 ranges_suc[cond.let],
106 .{ l, thresh - 1 },
107 );
108 ranges_rej[cond.let] = nextRange(
109 ranges_rej[cond.let],
110 .{ thresh, r },
111 );
112 } else {
113 ranges_suc[cond.let] = nextRange(
114 ranges_suc[cond.let],
115 .{ thresh + 1, r },
116 );
117 ranges_rej[cond.let] = nextRange(
118 ranges_rej[cond.let],
119 .{ l, thresh },
120 );
121 }
122 ans += rangeCombs(rules, rule.res, ranges_suc);
123 } else {
124 ans += rangeCombs(rules, rule.res, ranges_rej);
125 }
126 }
127
128 return ans;
129 }
130};
131
132const Range = [2]u64;
133const Ranges = [4]Range;
134const Ratings = []u64;
135const RuleList = []const Rule;
136const RuleGraph = std.StringHashMap(RuleList);
137
138fn nextRange(lhs: Range, rhs: Range) Range {
139 return Range{
140 @max(lhs[0], rhs[0]),
141 @min(lhs[1], rhs[1]),
142 };
143}
144
145pub fn solve(rules: RuleGraph, parts: []const Ratings) !void {
146 var part1: u64 = 0;
147 for (parts) |ratings| {
148 var curr: []const u8 = "in";
149
150 const accepted = while (true) {
151 if (mem.eql(u8, curr, "A")) {
152 break true;
153 } else if (mem.eql(u8, curr, "R")) {
154 break false;
155 }
156 curr = Rule.getNext(rules.get(curr).?, ratings);
157 };
158
159 if (accepted) {
160 for (ratings) |r| {
161 part1 += r;
162 }
163 }
164 }
165
166 print("{d}\n", .{part1});
167
168 const ranges = [_]Range{Range{ 1, 4000 }} ** 4;
169 const ans = Rule.rangeCombs(rules, "in", ranges);
170
171 print("{d}\n", .{ans});
172}
173
174pub fn main() !void {
175 var segments = mem.tokenizeSequence(u8, fin, "\n\n");
176 var splitRules = mem.tokenizeScalar(u8, segments.next().?, '\n');
177 var splitWorkflows = mem.tokenizeScalar(u8, segments.next().?, '\n');
178 var allRules = RuleGraph.init(allocator);
179 var parts = ArrayList(Ratings).init(allocator);
180
181 while (splitRules.next()) |line| {
182 var list = mem.tokenizeAny(u8, line, "{},");
183 const id = list.next().?;
184 var rules = ArrayList(Rule).init(allocator);
185
186 while (list.next()) |ruleStr| {
187 try rules.append(Rule.parse(ruleStr));
188 }
189 try allRules.put(id, rules.items);
190 }
191
192 while (splitWorkflows.next()) |line| {
193 var splitRatings = mem.tokenizeAny(u8, line, "{=,xmas}");
194 var ratings = ArrayList(u64).init(allocator);
195
196 while (splitRatings.next()) |r| {
197 try ratings.append(try std.fmt.parseInt(u64, r, 10));
198 }
199
200 try parts.append(ratings.items);
201 }
202
203 try solve(allRules, parts.items);
204}