solve day 24
This commit is contained in:
parent
e97c8e44bf
commit
d52e9bc73e
@ -275,7 +275,7 @@ fn compareQueueItem(_: void, a: QueueItem, b: QueueItem) std.math.Order {
|
|||||||
return std.math.order(b.distance, a.distance);
|
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 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].?;
|
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
|
.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 };
|
return .{ .uint = answer };
|
||||||
}
|
}
|
||||||
@ -392,7 +392,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
.y = map.height-1
|
.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 };
|
return .{ .uint = answer };
|
||||||
}
|
}
|
||||||
|
246
src/day24.zig
Normal file
246
src/day24.zig
Normal 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 };
|
||||||
|
}
|
@ -44,6 +44,7 @@ const Days = [_]aoc.Day{
|
|||||||
create_day(@import("./day21.zig")),
|
create_day(@import("./day21.zig")),
|
||||||
create_day(@import("./day22.zig")),
|
create_day(@import("./day22.zig")),
|
||||||
create_day(@import("./day23.zig")),
|
create_day(@import("./day23.zig")),
|
||||||
|
create_day(@import("./day24.zig")),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn kilobytes(count: u32) u32 {
|
fn kilobytes(count: u32) u32 {
|
||||||
@ -210,4 +211,5 @@ test {
|
|||||||
_ = @import("./day21.zig");
|
_ = @import("./day21.zig");
|
||||||
_ = @import("./day22.zig");
|
_ = @import("./day22.zig");
|
||||||
_ = @import("./day23.zig");
|
_ = @import("./day23.zig");
|
||||||
|
_ = @import("./day24.zig");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user