diff --git a/src/day03.zig b/src/day03.zig new file mode 100644 index 0000000..0f1993d --- /dev/null +++ b/src/day03.zig @@ -0,0 +1,145 @@ +const std = @import("std"); +const util = @import("util.zig"); + +pub fn main() !void { + const input = @embedFile("data/day03.txt"); + const sln = try solve(util.gpa, input); + std.debug.print("{d}\n", .{sln.sum}); + std.debug.print("{d}\n", .{sln.gears}); +} + +const Part = struct { + value: usize, + line: usize, + start: usize, + end: usize, +}; + +const Symbol = struct { char: u8, line: usize, offset: usize }; + +const Solution = struct { + sum: usize, + gears: usize, +}; + +fn absDiff(comptime T: type, a: T, b: T) T { + return @max(a, b) - @min(a, b); +} + +fn solve(alloc: std.mem.Allocator, input: []const u8) !Solution { + var parts = std.ArrayList(Part).init(alloc); + var symbols = std.ArrayList(Symbol).init(alloc); + + defer parts.deinit(); + defer symbols.deinit(); + + // Accumulate parts and symbols. + var it = std.mem.splitScalar(u8, input, '\n'); + var lnum: usize = 0; + while (it.next()) |line| : (lnum += 1) { + var current: Part = undefined; + var in_part = false; + + var offset: usize = 0; + for (line) |char| { + switch (char) { + '0'...'9' => { + if (!in_part) { + current.line = lnum; + current.start = offset; + current.value = 0; + in_part = true; + } + current.value = current.value * 10 + (char - '0'); + }, + '.' => { + if (in_part) { + current.end = offset - 1; + try parts.append(current); + } + in_part = false; + }, + else => { + if (in_part) { + current.end = offset - 1; + try parts.append(current); + } + in_part = false; + try symbols.append(.{ .char = char, .line = lnum, .offset = offset }); + }, + } + offset += 1; + } + + // Handle number at the end of a line. + if (in_part) { + current.end = offset - 1; + try parts.append(current); + } + } + + // Locate actual parts. + var sum: usize = 0; + var gear_sum: usize = 0; + var gears = std.AutoHashMap(Symbol, Part).init(alloc); + defer gears.deinit(); + + ploop: for (parts.items) |part| { + // std.debug.print("part {d}x{d}..{d}\n", .{ part.line, part.start, part.end }); + for (symbols.items) |symbol| { + // std.debug.print("symbol {d}x{d}\n", .{ symbol.line, symbol.offset }); + const inOffset = part.start <= symbol.offset + 1 and part.end + 1 >= symbol.offset; + const inLine = absDiff(usize, part.line, symbol.line) <= 1; + if (inOffset and inLine) { + // std.debug.print("part {d}x({d}..{d})={d}\n", .{ part.line, part.start, part.end, part.value }); + sum += part.value; + + // Detect gears. + if (symbol.char == '*') { + if (gears.get(symbol)) |other_gear| { + gear_sum += part.value * other_gear.value; + _ = gears.remove(symbol); + } else { + try gears.put(symbol, part); + } + } + + continue :ploop; + } + } + } + + return .{ .sum = sum, .gears = gear_sum }; +} + +test "silver" { + const input = + \\467..114.. + \\...*...... + \\..35..633. + \\......#... + \\617*...... + \\.....+.58. + \\..592..... + \\......755. + \\...$.*.... + \\664..598.. + ; + try std.testing.expectEqual(@as(usize, 4361), (try solve(std.testing.allocator, input)).sum); +} + +test "gold" { + const input = + \\467..114.. + \\...*...... + \\..35..633. + \\......#... + \\617*...... + \\.....+.58. + \\..592..... + \\......755. + \\...$.*.... + \\.664.598.. + ; + try std.testing.expectEqual(@as(usize, 467835), (try solve(std.testing.allocator, input)).gears); +}