aboutsummaryrefslogtreecommitdiffstats
path: root/day05/solution.zig
diff options
context:
space:
mode:
Diffstat (limited to 'day05/solution.zig')
-rw-r--r--day05/solution.zig276
1 files changed, 270 insertions, 6 deletions
diff --git a/day05/solution.zig b/day05/solution.zig
index 059a120..c22af8b 100644
--- a/day05/solution.zig
+++ b/day05/solution.zig
@@ -9,15 +9,279 @@ const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace);
9var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 9var gpa = std.heap.GeneralPurposeAllocator(.{}){};
10const allocator = gpa.allocator(); 10const allocator = gpa.allocator();
11 11
12pub fn part1() void { 12//
13 // Part 1 goes here 13// I represent the input in the form of:
14//
15// Input = [
16// Map [
17// Translation {
18// src = Range {
19// begin,
20// len,
21// },
22// dst = Range {
23// begin,
24// len,
25// }
26// }
27// ...
28// ]
29// ...
30// ]
31//
32// I filled unmapped areas with identity maps (1-1 translation src -> dest)
33// to avoid edge cases.
34//
35//
36const Range = struct {
37 begin: i64,
38 len: i64,
39};
40
41const Translation = struct {
42 src: Range,
43 dst: Range,
44};
45
46const Map = struct {
47 ranges: ArrayList(Translation),
48
49 pub fn init() Map {
50 const ranges = ArrayList(Translation).init(allocator);
51
52 return Map{
53 .ranges = ranges,
54 };
55 }
56
57 pub fn deinit(self: *@This()) void {
58 self.ranges.deinit();
59 }
60};
61
62fn parseSeeds(text: []const u8) ArrayList(i64) {
63 var result = ArrayList(i64).init(allocator);
64 var textNumbers = mem.splitScalar(u8, text, ' ');
65
66 // Ignore header
67 _ = textNumbers.next();
68
69 while (textNumbers.next()) |textNum| {
70 const n = std.fmt.parseInt(i64, textNum, 10) catch unreachable;
71 result.append(n) catch unreachable;
72 }
73
74 return result;
75}
76
77fn parseTranslation(text: []const u8) Translation {
78 var splitNums = mem.splitScalar(u8, text, ' ');
79 const dst_begin = std.fmt.parseInt(i64, splitNums.next().?, 10) catch unreachable;
80 const src_begin = std.fmt.parseInt(i64, splitNums.next().?, 10) catch unreachable;
81 const len = std.fmt.parseInt(i64, splitNums.next().?, 10) catch unreachable;
82
83 return Translation{
84 .src = .{
85 .begin = src_begin,
86 .len = len,
87 },
88 .dst = .{
89 .begin = dst_begin,
90 .len = len,
91 },
92 };
93}
94
95fn translationLess(@"_": void, lhs: Translation, rhs: Translation) bool {
96 return rangeLess(@"_", lhs.src, rhs.src);
97}
98
99fn parseMap(text: []const u8) Map {
100 var splitLines = mem.splitScalar(u8, text, '\n');
101 var translations = ArrayList(Translation).init(allocator);
102 defer translations.deinit();
103
104 // Ignore header
105 _ = splitLines.next();
106
107 while (splitLines.next()) |line| {
108 translations.append(parseTranslation(line)) catch unreachable;
109 }
110 mem.sort(Translation, translations.items, {}, translationLess);
111
112 var gapFilled = ArrayList(Translation).init(allocator);
113 const items = translations.items;
114
115 // Fill gap from 0 up to the first segment
116 if (items[0].src.begin > 0) {
117 gapFilled.append(.{ .src = .{
118 .begin = 0,
119 .len = items[0].src.begin,
120 }, .dst = .{
121 .begin = 0,
122 .len = items[0].src.begin,
123 } }) catch unreachable;
124 }
125
126 // Fill intermediate gaps
127 for (items[0 .. items.len - 1], 0..) |tr, i| {
128 const curr = tr.src;
129 const next = items[i + 1].src;
130 gapFilled.append(items[i]) catch unreachable;
131 if (rangeAdjacent(curr, next) == null) {
132 gapFilled.append(.{ .src = .{
133 .begin = curr.begin + curr.len,
134 .len = next.begin - curr.begin - curr.len,
135 }, .dst = .{
136 .begin = curr.begin + curr.len,
137 .len = next.begin - curr.begin - curr.len,
138 } }) catch unreachable;
139 }
140 }
141
142 // Fill gap from the end of the last segment to +infinity
143 const len = items.len;
144 gapFilled.append(.{ .src = .{
145 .begin = items[len - 1].src.begin,
146 .len = 4_000_000_000,
147 }, .dst = .{
148 .begin = items[len - 1].src.begin,
149 .len = 4_000_000_000,
150 } }) catch unreachable;
151
152 return Map{
153 .ranges = gapFilled,
154 };
155}
156
157fn evalSeed(seed: i64, maps: []const Map) i64 {
158 var s = seed;
159
160 for (maps) |map| {
161 for (map.ranges.items) |range| {
162 if (range.src.begin <= s and s < range.src.begin + range.src.len) {
163 s += range.dst.begin - range.src.begin;
164 break;
165 }
166 }
167 }
168
169 return s;
170}
171
172fn rangeLess(_: void, lhs: Range, rhs: Range) bool {
173 return lhs.begin < rhs.begin or (lhs.begin == rhs.begin and lhs.len > rhs.len);
174}
175
176fn rangeMin(lhs: Range, rhs: Range) Range {
177 return if (rangeLess({}, lhs, rhs)) lhs else rhs;
14} 178}
15 179
16pub fn part2() void { 180fn rangeMax(lhs: Range, rhs: Range) Range {
17 // Part 2 goes here 181 return if (rangeLess({}, lhs, rhs)) rhs else lhs;
182}
183
184fn rangeOverlap(lhs: Range, rhs: Range) ?Range {
185 const min = rangeMin(lhs, rhs);
186 const max = rangeMax(lhs, rhs);
187
188 if (min.begin + min.len <= max.begin) {
189 return null;
190 }
191
192 return Range{
193 .begin = max.begin,
194 .len = @min(min.begin + min.len - max.begin, max.len),
195 };
196}
197
198fn rangeAdjacent(lhs: Range, rhs: Range) ?Range {
199 const min = rangeMin(lhs, rhs);
200 const max = rangeMax(lhs, rhs);
201
202 if (min.begin + min.len != max.begin) {
203 return null;
204 }
205
206 return Range{
207 .begin = max.begin,
208 .len = @min(min.begin + min.len - max.begin, max.len),
209 };
210}
211
212pub fn part1(seeds: []const i64, maps: []const Map) void {
213 var ans: i64 = std.math.maxInt(i64);
214
215 for (seeds) |seed| {
216 const s = evalSeed(seed, maps);
217 ans = @min(ans, s);
218 }
219
220 print("{d}\n", .{ans});
221}
222
223fn evalNextState(m: Map, ranges: ArrayList(Range)) ArrayList(Range) {
224 var result = ArrayList(Range).init(allocator);
225
226 // Translate all seed range(s)
227 for (ranges.items) |r| {
228
229 // For every translation
230 for (m.ranges.items) |t| {
231 const overlap = rangeOverlap(t.src, r) orelse continue;
232
233 result.append(.{
234 .begin = t.dst.begin + overlap.begin - t.src.begin,
235 .len = overlap.len,
236 }) catch unreachable;
237 }
238 }
239
240 assert(result.items.len > 0);
241 mem.sort(Range, result.items, {}, rangeLess);
242
243 return result;
244}
245
246fn evalLowest(startRange: Range, maps: []const Map) i64 {
247 var ranges = ArrayList(Range).init(allocator);
248 defer ranges.deinit();
249
250 // Start with a single seed range
251 ranges.append(startRange) catch unreachable;
252
253 // For every map
254 for (maps) |m| {
255 const nextRanges = evalNextState(m, ranges);
256 ranges.deinit();
257 ranges = nextRanges;
258 }
259
260 return ranges.items[0].begin;
261}
262
263pub fn part2(seeds: []const i64, maps: []const Map) void {
264 var ans: i64 = std.math.maxInt(i64);
265
266 for (0..seeds.len / 2) |i| {
267 const begin: i64 = @intCast(seeds[i * 2]);
268 const len: i64 = @intCast(seeds[i * 2 + 1]);
269
270 ans = @min(ans, evalLowest(.{ .begin = begin, .len = len }, maps));
271 }
272 print("{d}\n", .{ans});
18} 273}
19 274
20pub fn main() !void { 275pub fn main() !void {
21 part1(); 276 var splitLines = mem.splitSequence(u8, fin, "\n\n");
22 part2(); 277 const seedsText = splitLines.next().?;
278 const seeds = parseSeeds(seedsText);
279
280 var maps = ArrayList(Map).init(allocator);
281 while (splitLines.next()) |mapText| {
282 maps.append(parseMap(mapText)) catch unreachable;
283 }
284
285 part1(seeds.items, maps.items);
286 part2(seeds.items, maps.items);
23} 287}