solve day 24

This commit is contained in:
Rokas Puzonas 2024-06-02 12:30:00 +03:00
parent e97c8e44bf
commit d52e9bc73e
3 changed files with 251 additions and 3 deletions

View File

@ -275,7 +275,7 @@ fn compareQueueItem(_: void, a: QueueItem, b: QueueItem) std.math.Order {
return std.math.order(b.distance, a.distance);
}
fn find_longest_path(allocator: Allocator, node_map: NodeMap, start: PointU32, end: PointU32, max_best_distances: ?u32) !u64 {
fn findLongestPath(allocator: Allocator, node_map: NodeMap, start: PointU32, end: PointU32, max_best_distances: ?u32) !u64 {
const start_region = node_map.region_map[node_map.width * start.y + start.x].?;
const end_region = node_map.region_map[node_map.width * end.y + end.x].?;
@ -361,7 +361,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
.y = map.height-1
};
const answer = try find_longest_path(allocator, node_map, start, end, null);
const answer = try findLongestPath(allocator, node_map, start, end, null);
return .{ .uint = answer };
}
@ -392,7 +392,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
.y = map.height-1
};
const answer = try find_longest_path(allocator, node_map, end, start, 1000);
const answer = try findLongestPath(allocator, node_map, end, start, 1000);
return .{ .uint = answer };
}

246
src/day24.zig Normal file
View File

@ -0,0 +1,246 @@
const aoc = @import("./aoc.zig");
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const Point = @import("./point.zig").Point;
const Axis = enum { X, Y, Z };
fn Vec3(comptime T: type) type {
return struct {
x: T,
y: T,
z: T,
fn project(self: @This(), axis: Axis) Point(T) {
return switch (axis) {
.X => Point(T){ .x = self.y, .y = self.z },
.Y => Point(T){ .x = self.x, .y = self.z },
.Z => Point(T){ .x = self.x, .y = self.y },
};
}
fn sub(self: @This(), other: @This()) @This() {
return @This(){
.x = self.x - other.x,
.y = self.y - other.y,
.z = self.z - other.z,
};
}
fn add(self: @This(), other: @This()) @This() {
return @This(){
.x = self.x + other.x,
.y = self.y + other.y,
.z = self.z + other.z,
};
}
fn mul(self: @This(), multiplier: T) @This() {
return @This(){
.x = self.x * multiplier,
.y = self.y * multiplier,
.z = self.z * multiplier,
};
}
fn cross(self: @This(), other: @This()) @This() {
return @This(){
.x = self.y * other.z - self.z * other.y,
.y = self.z * other.x - self.x * other.z,
.z = self.x * other.y - self.y * other.x,
};
}
fn dot(self: @This(), other: @This()) T {
return self.x * other.x + self.y * other.y + self.z * other.z;
}
fn cast(self: @This(), comptime to: type) Vec3(to) {
return Vec3(to){
.x = self.x,
.y = self.y,
.z = self.z,
};
}
};
}
const Hailstone = struct {
position: Vec3(i64),
velocity: Vec3(i64),
};
fn parseVec3(str: []const u8) !Vec3(i64) {
var components: [3]i64 = undefined;
var count: u32 = 0;
var iter = std.mem.splitScalar(u8, str, ',');
while (iter.next()) |part| {
if (count == 3) return error.TooManyComponents;
components[count] = try std.fmt.parseInt(i64, std.mem.trim(u8, part, " "), 10);
count += 1;
}
if (count < 3) {
return error.MissingComponents;
}
return Vec3(i64){
.x = components[0],
.y = components[1],
.z = components[2],
};
}
fn parseInput(allocator: Allocator, lines: []const []const u8) !std.ArrayList(Hailstone) {
var parsed = std.ArrayList(Hailstone).init(allocator);
errdefer parsed.deinit();
for (lines) |line| {
const separator = std.mem.indexOfScalar(u8, line, '@') orelse return error.NoSeparator;
try parsed.append(Hailstone{
.position = try parseVec3(line[0..separator]),
.velocity = try parseVec3(line[(separator+1)..])
});
}
return parsed;
}
fn getIntersect(p1: Point(f64), v1: Point(f64), p2: Point(f64), v2: Point(f64)) ?Point(f64) {
const t = -(
((p1.x - p2.x) * v2.y - (p1.y - p2.y) * v2.x) /
(v1.x * v2.y - v1.y * v2.x)
);
const u = (
(v1.x * (p1.y - p2.y) - v1.y * (p1.x - p2.x)) /
(v1.x * v2.y - v1.y * v2.x)
);
const px = p1.x + t * v1.x;
const py = p1.y + t * v1.y;
if (t >= 0 and u >= 0) {
return Point(f64){ .x = px, .y = py };
} else {
return null;
}
}
fn vec3FloatFromInt(p: Vec3(i64)) Vec3(f64) {
return Vec3(f64){
.x = @floatFromInt(p.x),
.y = @floatFromInt(p.y),
.z = @floatFromInt(p.z),
};
}
pub fn part1(input: *aoc.Input) !aoc.Result {
const hailstones = try parseInput(input.allocator, input.lines);
defer hailstones.deinit();
const min_test_area = 200000000000000;
const max_test_area = 400000000000000;
var answer: u32 = 0;
for (0..(hailstones.items.len-1)) |i| {
const hailstone1 = hailstones.items[i];
for ((i+1)..hailstones.items.len) |j| {
const hailstone2 = hailstones.items[j];
const p1 = vec3FloatFromInt(hailstone1.position);
const v1 = vec3FloatFromInt(hailstone1.velocity);
const p2 = vec3FloatFromInt(hailstone2.position);
const v2 = vec3FloatFromInt(hailstone2.velocity);
const p = getIntersect(
.{ .x = p1.x, .y = p1.y },
.{ .x = v1.x, .y = v1.y },
.{ .x = p2.x, .y = p2.y },
.{ .x = v2.x, .y = v2.y }
) orelse continue;
if (min_test_area <= p.x and p.x <= max_test_area and min_test_area <= p.y and p.y <= max_test_area) {
answer += 1;
}
}
}
return .{ .uint = answer };
}
fn getIntersectTime(comptime T: type, p1: Vec3(T), v1: Vec3(T), p2: Vec3(T), v2: Vec3(T)) !T {
const place_normal = p1.cross(p1.add(v1));
const denominator = v2.dot(place_normal);
if (denominator == 0) {
return error.Parallel;
}
const numerator = -p2.dot(place_normal);
if (@mod(numerator, denominator) != 0) {
return error.NonInteger;
}
return @divExact(numerator, denominator);
}
// This helped a LOT, really nice visualization and explanation of the math
// https://aidiakapi.com/blog/2024-01-20-advent-of-code-2023-day-24/
pub fn part2(input: *aoc.Input) !aoc.Result {
const hailstones = try parseInput(input.allocator, input.lines);
defer hailstones.deinit();
var answer: u64 = 0;
var triplet_iter = std.mem.window(Hailstone, hailstones.items, 3, 1);
while (triplet_iter.next()) |triplet| {
const r = triplet[0]; // Reference point
const a = triplet[1]; // A point
const b = triplet[2]; // B point
const r_p = r.position.cast(i128);
const r_v = r.velocity.cast(i128);
const a_p = a.position.cast(i128);
const a_v = a.velocity.cast(i128);
const b_p = b.position.cast(i128);
const b_v = b.velocity.cast(i128);
const ra_p = a_p.sub(r_p); // A position relative to reference
const rb_p = b_p.sub(r_p); // A velocity relative to reference
const ra_v = a_v.sub(r_v); // B position relative to reference
const rb_v = b_v.sub(r_v); // B velocity relative to reference
// Check "Plane vs. Line" intersection for A and B
const t1 = getIntersectTime(i128, rb_p, rb_v, ra_p, ra_v) catch continue;
const t2 = getIntersectTime(i128, ra_p, ra_v, rb_p, rb_v) catch continue;
// Convert the intersection time into a global position
// From this point on reference point is not needed
const c1 = a_p.add(a_v.mul(t1));
const c2 = b_p.add(b_v.mul(t2));
const delta_time = t2 - t1;
const delta_intersect = c2.sub(c1);
if (@mod(delta_intersect.x, delta_time) != 0) continue;
if (@mod(delta_intersect.y, delta_time) != 0) continue;
if (@mod(delta_intersect.z, delta_time) != 0) continue;
const v = Vec3(i128){
.x = @divExact(delta_intersect.x, delta_time),
.y = @divExact(delta_intersect.y, delta_time),
.z = @divExact(delta_intersect.z, delta_time),
};
const p = c1.sub(v.mul(t1));
answer = @intCast(p.x + p.y + p.z);
break;
}
return .{ .uint = answer };
}

View File

@ -44,6 +44,7 @@ const Days = [_]aoc.Day{
create_day(@import("./day21.zig")),
create_day(@import("./day22.zig")),
create_day(@import("./day23.zig")),
create_day(@import("./day24.zig")),
};
fn kilobytes(count: u32) u32 {
@ -210,4 +211,5 @@ test {
_ = @import("./day21.zig");
_ = @import("./day22.zig");
_ = @import("./day23.zig");
_ = @import("./day24.zig");
}