aoc2025 @ master

  1const std = @import("std");
  2
  3pub fn pt1(comptime input: []const u8) u64 {
  4    const r = input[0 .. input.len - 1];
  5    var ranges = std.mem.splitScalar(u8, r, ',');
  6
  7    var count: u64 = 0;
  8    while (ranges.next()) |l| {
  9        if (l.len == 0) {
 10            return count;
 11        }
 12
 13        var range = parseRange(l);
 14        while (range.next()) |v| {
 15            if (isRange(v)) {
 16                count += v;
 17            }
 18        }
 19    }
 20    return count;
 21}
 22
 23pub fn pt2(comptime input: []const u8) u64 {
 24    const r = input[0 .. input.len - 1];
 25    var ranges = std.mem.splitScalar(u8, r, ',');
 26
 27    var count: u64 = 0;
 28    while (ranges.next()) |l| {
 29        if (l.len == 0) {
 30            return count;
 31        }
 32
 33        var range = parseRange(l);
 34        while (range.next()) |v| {
 35            if (isRangeN(v)) {
 36                count += v;
 37            }
 38        }
 39    }
 40    return count;
 41}
 42
 43var buf: [20]u8 = undefined;
 44
 45fn isRangeN(v: u64) bool {
 46    const str = std.fmt.bufPrint(&buf, "{d}", .{v}) catch unreachable;
 47    for (1..str.len) |s| {
 48        // We can't check if this batch can not evenly chunked.
 49        if (@mod(str.len, s) != 0) {
 50            continue;
 51        }
 52
 53        const c = str.len / s; // size on the chunk
 54        const p = str[0..s]; // first chunk as reference for eql
 55        var m = true; // flag that says if something went wrong (if wrong set false)
 56
 57
 58        for (1..c) |i| {
 59            const sub_str = str[i * s .. ((1 + i) * s)];
 60            if (!std.mem.eql(u8, p, sub_str)) {
 61                m = false;
 62                break;
 63            }
 64        }
 65
 66        if (m) return true;
 67    }
 68
 69    return false;
 70}
 71
 72fn isRange(v: u64) bool {
 73    const str = std.fmt.bufPrint(&buf, "{d}", .{v}) catch unreachable;
 74
 75    if ((str.len % 2) != 0) {
 76        return false;
 77    }
 78
 79    const mid = str.len / 2;
 80
 81    for (0..mid) |i| {
 82        if (str[i] != str[i + mid]) {
 83            return false;
 84        }
 85    }
 86
 87    return true;
 88}
 89
 90const Range = struct {
 91    from: u64 = 0,
 92    to: u64 = 0,
 93    index: u64 = 0,
 94
 95    pub fn next(self: *Range) ?u64 {
 96        const to = self.from + self.index;
 97        if (to > self.to) {
 98            return null;
 99        }
100
101        self.index += 1;
102        return to;
103    }
104};
105
106fn parseRange(range_str: []const u8) Range {
107    var range: Range = .{};
108    var iter = std.mem.splitScalar(u8, range_str, '-');
109
110    var i: u64 = 0;
111    while (iter.next()) |v| {
112        if (i == 0) {
113            range.from = std.fmt.parseInt(u64, v, 10) catch unreachable;
114        } else if (i == 1) {
115            range.to = std.fmt.parseInt(u64, v, 10) catch unreachable;
116        } else {
117            break;
118        }
119
120        i += 1;
121    }
122
123    return range;
124}
125
126test "Day 2 part 1" {
127    const res = pt1(@embedFile("./input/day2"));
128    try std.testing.expect(res == 55916882972);
129}
130
131test "Day 2 part 2" {
132    const res = pt2(@embedFile("./input/day2"));
133    try std.testing.expect(res == 76169125915);
134}