Skip to content

Commit

Permalink
2024 day 8
Browse files Browse the repository at this point in the history
  • Loading branch information
agagniere committed Dec 8, 2024
1 parent dc44f24 commit 5da1631
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 3 deletions.
10 changes: 10 additions & 0 deletions 2024/08/README.md
Original file line number Diff line number Diff line change
@@ -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
```
41 changes: 41 additions & 0 deletions 2024/08/build.zig
Original file line number Diff line number Diff line change
@@ -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);
}
}
11 changes: 11 additions & 0 deletions 2024/08/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.{
.name = "day08",
.version = "0.1.0",
.dependencies = .{
.utils = .{
.url = "../utils/",
.hash = "122059863bba3b73097c2905eacfa772e5c9ad7cd6616270b08dfdf41e3d81900420",
},
},
.paths = .{ "build.zig", "build.zig.zon", "solve.zig" },
}
159 changes: 159 additions & 0 deletions 2024/08/solve.zig
Original file line number Diff line number Diff line change
@@ -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));
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:|
Expand All @@ -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

0 comments on commit 5da1631

Please sign in to comment.