From b863e1b2aa3105ea8077f0bf18b3d88f699d0faf Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Fri, 8 Dec 2023 22:03:02 +0200 Subject: [PATCH] solve day 3 --- src/day3.zig | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 4 +- src/point.zig | 12 +++++ 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src/day3.zig create mode 100644 src/point.zig diff --git a/src/day3.zig b/src/day3.zig new file mode 100644 index 0000000..b38a586 --- /dev/null +++ b/src/day3.zig @@ -0,0 +1,146 @@ +const aoc = @import("./aoc.zig"); +const std = @import("std"); +const Point = @import("./point.zig").Point; + +const PointU32 = Point(u32); +const PointList = std.ArrayList(PointU32); + +fn find_number_start(line: []const u8, from: u32) u32 { + var i = from; + while (i > 0) : (i -= 1) { + if (!std.ascii.isDigit(line[i-1])) { break; } + } + return i; +} + +fn find_number_end(line: []const u8, from: u32) u32 { + var i = from; + while (i < line.len-1) : (i += 1) { + if (!std.ascii.isDigit(line[i+1])) { break; } + } + return i+1; +} + +fn append_unique(list: *PointList, point: PointU32) !void { + for (list.items) |item| { + if (item.eql(&point)) { return; } + } + try list.append(point); +} + +fn find_neighbouring_numbers(found_list: *PointList, lines: [][]const u8, x: usize, y: usize) !void { + const neighbours = .{ + .{ -1, -1 }, .{ 0, -1 }, .{ 1, -1 }, + .{ -1, 0 }, .{ 1, 0 }, + .{ -1, 1 }, .{ 0, 1 }, .{ 1, 1 }, + }; + + inline for (neighbours) |offset| { + const nx = @as(i32, @intCast(x)) + offset[0]; + const ny = @as(i32, @intCast(y)) + offset[1]; + if ((nx >= 0 or ny >= 0 or nx < lines[y].len or ny < lines.len)) { + const nx_u32: u32 = @intCast(nx); + const ny_u32: u32 = @intCast(ny); + if (std.ascii.isDigit(lines[ny_u32][nx_u32])) { + const number_start = find_number_start(lines[ny_u32], nx_u32); + try append_unique(found_list, .{ .x = number_start, .y = ny_u32 }); + } + } + } +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + const lines = input.lines; + const allocator = input.allocator; + + var numbers = PointList.init(allocator); + defer numbers.deinit(); + + for (lines, 0..) |line, y| { + for (line, 0..) |c, x| { + if (std.ascii.isDigit(c) or c == '.') { continue; } + + try find_neighbouring_numbers(&numbers, lines, x, y); + } + } + + var sum: u32 = 0; + for (numbers.items) |number_pos| { + const line = lines[number_pos.y]; + const number_end = find_number_end(line, number_pos.x); + const number_str = line[number_pos.x..number_end]; + sum += try std.fmt.parseInt(u32, number_str, 10); + } + + return .{ .uint = sum }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + const lines = input.lines; + const allocator = input.allocator; + + var sum: u32 = 0; + for (lines, 0..) |line, y| { + for (line, 0..) |c, x| { + if (c != '*') { continue; } + + var numbers = PointList.init(allocator); + defer numbers.deinit(); + + try find_neighbouring_numbers(&numbers, lines, x, y); + + if (numbers.items.len != 2) continue; + + var gear_ratio: u32 = 1; + for (numbers.items) |number_pos| { + const number_line = lines[number_pos.y]; + const number_end = find_number_end(number_line, number_pos.x); + const number_str = number_line[number_pos.x..number_end]; + const number = try std.fmt.parseInt(u32, number_str, 10); + gear_ratio *= number; + } + + sum += gear_ratio; + } + } + + return .{ .uint = sum }; +} + +test "part 1 example" { + var lines = [_][]const u8{ + "467..114..", + "...*......", + "..35..633.", + "......#...", + "617*......", + ".....+.58.", + "..592.....", + "......755.", + "...$.*....", + ".664.598.." + }; + var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator }; + var actual = try part1(&input); + var expected: u32 = 4361; + try std.testing.expectEqual(expected, actual.uint); +} + +test "part 2 example" { + var lines = [_][]const u8{ + "467..114..", + "...*......", + "..35..633.", + "......#...", + "617*......", + ".....+.58.", + "..592.....", + "......755.", + "...$.*....", + ".664.598.." + }; + var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator }; + var actual = try part2(&input); + var expected: u32 = 467835; + try std.testing.expectEqual(expected, actual.uint); +} diff --git a/src/main.zig b/src/main.zig index ea9260c..81e7322 100644 --- a/src/main.zig +++ b/src/main.zig @@ -21,7 +21,8 @@ fn create_day(comptime module: anytype) aoc.Day { const Days = [_]aoc.Day{ create_day(@import("day1.zig")), - create_day(@import("day2.zig")) + create_day(@import("day2.zig")), + create_day(@import("day3.zig")) }; pub fn main() !u8 { @@ -98,4 +99,5 @@ pub fn main() !u8 { test { _ = @import("day1.zig"); _ = @import("day2.zig"); + _ = @import("day3.zig"); } diff --git a/src/point.zig b/src/point.zig new file mode 100644 index 0000000..1816b0a --- /dev/null +++ b/src/point.zig @@ -0,0 +1,12 @@ +pub fn Point(comptime T: type) type { + return struct { + const Self = @This(); + + x: T, + y: T, + + pub fn eql(self: *const Self, other: *const Self) bool { + return self.x == other.x and self.y == other.y; + } + }; +}