From 5da1631b3d835fc2585cfb4411568ddc07fc7f9c Mon Sep 17 00:00:00 2001 From: Antoine Date: Sun, 8 Dec 2024 20:22:10 +0100 Subject: [PATCH] 2024 day 8 --- 2024/08/README.md | 10 +++ 2024/08/build.zig | 41 +++++++++++ 2024/08/build.zig.zon | 11 +++ 2024/08/solve.zig | 159 ++++++++++++++++++++++++++++++++++++++++++ README.md | 6 +- 5 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 2024/08/README.md create mode 100644 2024/08/build.zig create mode 100644 2024/08/build.zig.zon create mode 100644 2024/08/solve.zig diff --git a/2024/08/README.md b/2024/08/README.md new file mode 100644 index 0000000..287d983 --- /dev/null +++ b/2024/08/README.md @@ -0,0 +1,10 @@ +# Day 8 + +[Statement](https://adventofcode.com/2024/day/8) + +```console +$ zig build --release run < input.txt +Number of 2:1 antinodes : xxx +Number of antinodes : xxxx +parse: 12.791us, part1: 6.875us, part2: 175.917us +``` diff --git a/2024/08/build.zig b/2024/08/build.zig new file mode 100644 index 0000000..0adc64f --- /dev/null +++ b/2024/08/build.zig @@ -0,0 +1,41 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast }); + + const utils = b.dependency("utils", .{ .target = target, .optimize = optimize }).module("utils"); + + const exe = b.addExecutable(.{ + .name = "day8", + .root_source_file = b.path("solve.zig"), + .target = target, + .optimize = optimize, + }); + + exe.root_module.addImport("utils", utils); + b.installArtifact(exe); + + { // Run + const run_step = b.step("run", "Run the app"); + const run_cmd = b.addRunArtifact(exe); + + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + run_step.dependOn(&run_cmd.step); + } + { // Test + const test_step = b.step("test", "Run unit tests"); + const unit_tests = b.addTest(.{ + .root_source_file = b.path("solve.zig"), + .target = target, + .optimize = optimize, + }); + const run_unit_tests = b.addRunArtifact(unit_tests); + + unit_tests.root_module.addImport("utils", utils); + test_step.dependOn(&run_unit_tests.step); + } +} diff --git a/2024/08/build.zig.zon b/2024/08/build.zig.zon new file mode 100644 index 0000000..9ffafe6 --- /dev/null +++ b/2024/08/build.zig.zon @@ -0,0 +1,11 @@ +.{ + .name = "day08", + .version = "0.1.0", + .dependencies = .{ + .utils = .{ + .url = "../utils/", + .hash = "122059863bba3b73097c2905eacfa772e5c9ad7cd6616270b08dfdf41e3d81900420", + }, + }, + .paths = .{ "build.zig", "build.zig.zon", "solve.zig" }, +} diff --git a/2024/08/solve.zig b/2024/08/solve.zig new file mode 100644 index 0000000..cf91778 --- /dev/null +++ b/2024/08/solve.zig @@ -0,0 +1,159 @@ +const std = @import("std"); +const utils = @import("utils"); + +const Tuple = std.meta.Tuple; +const Allocator = std.mem.Allocator; +const PointList = std.ArrayListUnmanaged(Point); +const PointSet = std.AutoHashMapUnmanaged(Point, void); +const PointsPerFrequency = std.AutoHashMapUnmanaged(u8, PointList); + +const Point = struct { + x: i32, + y: i32, + + pub fn add(self: Point, other: Point) Point { + return .{ .x = self.x + other.x, .y = self.y + other.y }; + } + + pub fn sub(self: Point, other: Point) Point { + return .{ .x = self.x - other.x, .y = self.y - other.y }; + } + + pub fn isInBounds(self: Point, bounds: Point) bool { + return self.x >= 0 and self.y >= 0 and self.x < bounds.x and self.y < bounds.y; + } +}; + +pub fn parse(allocator: Allocator, input: anytype) !Tuple(&.{ PointsPerFrequency, Point }) { + var antennas: PointsPerFrequency = .empty; + var bound_x: i32 = 0; + var y: i32 = 0; + var lines = utils.lineIterator(input); + + try antennas.ensureTotalCapacity(allocator, 26 * 2 + 10); + while (lines.next()) |line| { + bound_x = @intCast(line.len); + for (line, 0..line.len) |c, x| { + if (c != '.') { + if (antennas.getPtr(c)) |list| { + list.appendAssumeCapacity(.{ .x = @intCast(x), .y = y }); + } else { + var list = try PointList.initCapacity(allocator, 16); + list.appendAssumeCapacity(.{ .x = @intCast(x), .y = y }); + antennas.putAssumeCapacity(c, list); + } + } + } + y += 1; + } + return .{ antennas, .{ .x = bound_x, .y = y } }; +} + +const AntinodeIterator = struct { + a: Point, + b: Point, + delta: Point, + + pub fn init(p1: Point, p2: Point) AntinodeIterator { + return .{ .a = p1, .b = p2, .delta = p2.sub(p1) }; + } + + pub fn next(self: *AntinodeIterator, bounds: Point) ?Point { + var result: ?Point = null; + if (self.a.isInBounds(bounds)) { + result = self.a; + self.a = self.a.sub(self.delta); + } else if (self.b.isInBounds(bounds)) { + result = self.b; + self.b = self.b.add(self.delta); + } + return result; + } +}; + +fn part1(allocator: Allocator, antennas: PointsPerFrequency, bounds: Point) u64 { + var frequencies = antennas.iterator(); + var antinodes: PointSet = .empty; + + antinodes.ensureTotalCapacity(allocator, @intCast(@divTrunc(bounds.x * bounds.y, 10))) catch unreachable; + while (frequencies.next()) |frequency| { + const positions = frequency.value_ptr.items; + for (0..positions.len, positions) |i, p1| { + for (positions[i + 1 ..]) |p2| { + const nodes = [_]Point{ p1.sub(p2.sub(p1)), p2.add(p2.sub(p1)) }; + for (nodes) |node| { + if (node.isInBounds(bounds)) + antinodes.putAssumeCapacity(node, {}); + } + } + } + } + return antinodes.count(); +} + +fn part2(allocator: Allocator, antennas: PointsPerFrequency, bounds: Point) u64 { + var frequencies = antennas.iterator(); + var antinodes: PointSet = .empty; + + antinodes.ensureTotalCapacity(allocator, @intCast(@divTrunc(bounds.x * bounds.y, 4))) catch unreachable; + while (frequencies.next()) |frequency| { + const positions = frequency.value_ptr.items; + for (0..positions.len, positions) |i, p1| { + for (positions[i + 1 ..]) |p2| { + var nodes = AntinodeIterator.init(p1, p2); + while (nodes.next(bounds)) |node| { + antinodes.putAssumeCapacity(node, {}); + } + } + } + } + + return antinodes.count(); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var stdin = std.io.bufferedReader(std.io.getStdIn().reader()); + var timer = try std.time.Timer.start(); + var times: [3]u64 = undefined; + + const antennas, const bounds = try parse(alloc, stdin.reader()); + times[0] = timer.lap(); + const one = part1(alloc, antennas, bounds); + times[1] = timer.lap(); + const two = part2(alloc, antennas, bounds); + times[2] = timer.lap(); + + std.debug.print("Number of 2:1 antinodes : {:8}\n", .{one}); + std.debug.print("Number of antinodes : {:8}\n", .{two}); + std.debug.print("parse: {}, part1: {}, part2: {}\n", .{ std.fmt.fmtDuration(times[0]), std.fmt.fmtDuration(times[1]), std.fmt.fmtDuration(times[2]) }); +} + +test { + const sample = + \\............ + \\........0... + \\.....0...... + \\.......0.... + \\....0....... + \\......A..... + \\............ + \\............ + \\........A... + \\.........A.. + \\............ + \\............ + ; + var stream = std.io.fixedBufferStream(sample); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const antennas, const bounds = try parse(alloc, stream.reader()); + + try std.testing.expectEqual(14, part1(alloc, antennas, bounds)); + try std.testing.expectEqual(34, part2(alloc, antennas, bounds)); +} diff --git a/README.md b/README.md index 5fa44e8..ad9b7c9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ My solutions for the [Advent of Code](https://adventofcode.com), a challenge sta | 05 |:star::star:|:star::star:||:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star: | 06 ||:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:|:star::star: | 07 ||:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:|:star::star: -| 08 |||||:star::star:|:star::star:|:star::star:|:star::star:| +| 08 |||||:star::star:|:star::star:|:star::star:|:star::star:|:star::star: | 09 |||||:star::star:|:star::star:|:star::star:|:star::star:| | 10 |||||:star::star:|:star::star:|:star::star:|:star::star:| | 11 |||||:star::star:|:star::star:|:star::star:|:star::star:| @@ -30,6 +30,6 @@ My solutions for the [Advent of Code](https://adventofcode.com), a challenge sta | 23 |||||||:star::star:|:star:| | 24 ||||||:star::star:|:star::star:|:star:| | 25 |||||:star:|:star:|:star::star:|| -| Total | 10 | 14 | 4 | 10 | 42 | 44 | 50 | 44 | 14 +| Total | 10 | 14 | 4 | 10 | 42 | 44 | 50 | 44 | 16 -Total stars: 232 +Total stars: 234