aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOrfeas <38209077+0xfea5@users.noreply.github.com>2023-12-07 06:56:53 +0200
committerOrfeas <38209077+0xfea5@users.noreply.github.com>2025-10-28 23:20:45 +0200
commit3ffc9de9efd3b3650eaf3ccc77e434fa000a9a08 (patch)
tree49aee2fcbb4a5f7a57a67ea49b6b0f3cefa35117
parentcreate template file and update init script (diff)
downloadaoc23-3ffc9de9efd3b3650eaf3ccc77e434fa000a9a08.tar.gz
aoc23-3ffc9de9efd3b3650eaf3ccc77e434fa000a9a08.zip
day5
-rw-r--r--day02/solution.zig6
-rw-r--r--day04/solution.zig6
-rw-r--r--day05/example.txt33
-rw-r--r--day05/solution.zig276
4 files changed, 309 insertions, 12 deletions
diff --git a/day02/solution.zig b/day02/solution.zig
index 5e8d6f6..b40274d 100644
--- a/day02/solution.zig
+++ b/day02/solution.zig
@@ -45,10 +45,10 @@ pub fn parseBall(text: []const u8) Ball {
45 const trimmed = mem.trim(u8, text, &std.ascii.whitespace); 45 const trimmed = mem.trim(u8, text, &std.ascii.whitespace);
46 var splitBalls = mem.splitScalar(u8, trimmed, ' '); 46 var splitBalls = mem.splitScalar(u8, trimmed, ' ');
47 47
48 const nballs = std.fmt.parseInt(u64, splitBalls.next() orelse unreachable, 10) catch unreachable; 48 const nballs = std.fmt.parseInt(u64, splitBalls.next().?, 10) catch unreachable;
49 var color: Color = undefined; 49 var color: Color = undefined;
50 color = blk: { 50 color = blk: {
51 const s = splitBalls.next() orelse unreachable; 51 const s = splitBalls.next().?;
52 52
53 if (mem.eql(u8, s, "red")) { 53 if (mem.eql(u8, s, "red")) {
54 break :blk .red; 54 break :blk .red;
@@ -152,7 +152,7 @@ pub fn main() !void {
152 var splitGame = mem.splitScalar(u8, line, ':'); 152 var splitGame = mem.splitScalar(u8, line, ':');
153 _ = splitGame.next(); 153 _ = splitGame.next();
154 154
155 const game = try parseGame(splitGame.next() orelse unreachable); 155 const game = try parseGame(splitGame.next().?);
156 try games.append(game); 156 try games.append(game);
157 } 157 }
158 158
diff --git a/day04/solution.zig b/day04/solution.zig
index 081277a..6f67569 100644
--- a/day04/solution.zig
+++ b/day04/solution.zig
@@ -39,12 +39,12 @@ fn parseCard(text: []const u8) !Card {
39 39
40 var prefix = mem.splitScalar(u8, text, ':'); 40 var prefix = mem.splitScalar(u8, text, ':');
41 _ = prefix.next(); // ignore 'Card ##' prefix 41 _ = prefix.next(); // ignore 'Card ##' prefix
42 const postfix = prefix.next() orelse unreachable; 42 const postfix = prefix.next().?;
43 43
44 var allNumbers = mem.splitScalar(u8, postfix, '|'); 44 var allNumbers = mem.splitScalar(u8, postfix, '|');
45 45
46 var winningText = mem.splitScalar(u8, allNumbers.next() orelse unreachable, ' '); 46 var winningText = mem.splitScalar(u8, allNumbers.next().?, ' ');
47 var ownedText = mem.splitScalar(u8, allNumbers.next() orelse unreachable, ' '); 47 var ownedText = mem.splitScalar(u8, allNumbers.next().?, ' ');
48 // split produces some empty strings that must be ignored later 48 // split produces some empty strings that must be ignored later
49 49
50 while (winningText.next()) |numText| { 50 while (winningText.next()) |numText| {
diff --git a/day05/example.txt b/day05/example.txt
new file mode 100644
index 0000000..f756727
--- /dev/null
+++ b/day05/example.txt
@@ -0,0 +1,33 @@
1seeds: 79 14 55 13
2
3seed-to-soil map:
450 98 2
552 50 48
6
7soil-to-fertilizer map:
80 15 37
937 52 2
1039 0 15
11
12fertilizer-to-water map:
1349 53 8
140 11 42
1542 0 7
1657 7 4
17
18water-to-light map:
1988 18 7
2018 25 70
21
22light-to-temperature map:
2345 77 23
2481 45 19
2568 64 13
26
27temperature-to-humidity map:
280 69 1
291 0 69
30
31humidity-to-location map:
3260 56 37
3356 93 4
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}