Compare commits
2 Commits
e97c8e44bf
...
4c30b52548
Author | SHA1 | Date | |
---|---|---|---|
4c30b52548 | |||
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 };
|
||||||
|
}
|
262
src/day25.zig
Normal file
262
src/day25.zig
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
const aoc = @import("./aoc.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const max_connections = 16;
|
||||||
|
const ComponentName = std.BoundedArray(u8, 3);
|
||||||
|
const WiringComponent = struct {
|
||||||
|
name: ComponentName,
|
||||||
|
connected_to: std.BoundedArray(ComponentName, max_connections),
|
||||||
|
|
||||||
|
pub fn format(
|
||||||
|
self: WiringComponent,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
options: std.fmt.FormatOptions,
|
||||||
|
writer: anytype,
|
||||||
|
) !void {
|
||||||
|
_ = fmt;
|
||||||
|
_ = options;
|
||||||
|
|
||||||
|
try writer.print("{s}:", .{ self.name.constSlice() });
|
||||||
|
for (self.connected_to.constSlice()) |other_name| {
|
||||||
|
try writer.print(" {s}", .{ other_name.constSlice() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn parseComponent(line: []const u8) !WiringComponent {
|
||||||
|
const name_separator = std.mem.indexOfScalar(u8, line, ':') orelse return error.NoSeparator;
|
||||||
|
var connected_to = std.BoundedArray(ComponentName, max_connections).init(0) catch unreachable;
|
||||||
|
|
||||||
|
var connected_iter = std.mem.splitScalar(u8, line[(name_separator+1)..], ' ');
|
||||||
|
while (connected_iter.next()) |part| {
|
||||||
|
const part_trimmed = std.mem.trim(u8, part, " ");
|
||||||
|
if (part_trimmed.len == 0) continue;
|
||||||
|
try connected_to.append(try ComponentName.fromSlice(part_trimmed));
|
||||||
|
}
|
||||||
|
|
||||||
|
return WiringComponent{
|
||||||
|
.name = try ComponentName.fromSlice(line[0..name_separator]),
|
||||||
|
.connected_to = connected_to
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !std.ArrayList(WiringComponent) {
|
||||||
|
var components = std.ArrayList(WiringComponent).init(allocator);
|
||||||
|
errdefer components.deinit();
|
||||||
|
|
||||||
|
for (lines) |line| {
|
||||||
|
try components.append(try parseComponent(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ComponentId = u11;
|
||||||
|
const ComponentGraph = struct {
|
||||||
|
const ConnectionList = std.BoundedArray(ComponentId, max_connections);
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
names: std.ArrayList(ComponentName),
|
||||||
|
connections: []ConnectionList,
|
||||||
|
|
||||||
|
fn init(allocator: Allocator, components: []WiringComponent) !ComponentGraph {
|
||||||
|
var names = std.ArrayList(ComponentName).init(allocator);
|
||||||
|
errdefer names.deinit();
|
||||||
|
|
||||||
|
for (components) |component| {
|
||||||
|
if (getComponentId(names.items, component.name.constSlice()) == null) {
|
||||||
|
try names.append(component.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (component.connected_to.constSlice()) |other_name| {
|
||||||
|
if (getComponentId(names.items, other_name.constSlice()) == null) {
|
||||||
|
try names.append(other_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const connections = try allocator.alloc(ConnectionList, names.items.len);
|
||||||
|
errdefer allocator.free(connections);
|
||||||
|
for (connections) |*connection| {
|
||||||
|
connection.* = ConnectionList.init(0) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const self = ComponentGraph{
|
||||||
|
.allocator = allocator,
|
||||||
|
.names = names,
|
||||||
|
.connections = connections
|
||||||
|
};
|
||||||
|
|
||||||
|
for (components) |component| {
|
||||||
|
const id: ComponentId = getComponentId(names.items, component.name.constSlice()) orelse unreachable;
|
||||||
|
for (component.connected_to.constSlice()) |other_name| {
|
||||||
|
const other_id = getComponentId(names.items, other_name.constSlice()) orelse unreachable;
|
||||||
|
|
||||||
|
if (std.mem.indexOfScalar(ComponentId, connections[id].constSlice(), other_id) == null) {
|
||||||
|
try connections[id].append(other_id);
|
||||||
|
}
|
||||||
|
if (std.mem.indexOfScalar(ComponentId, connections[other_id].constSlice(), id) == null) {
|
||||||
|
try connections[other_id].append(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getComponentId(names: []ComponentName, target: []const u8) ?ComponentId {
|
||||||
|
for (0.., names) |id, name| {
|
||||||
|
if (std.mem.eql(u8, target, name.constSlice())) {
|
||||||
|
return @intCast(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getName(self: ComponentGraph, id: ComponentId) []const u8 {
|
||||||
|
return self.names.items[id].constSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: ComponentGraph) void {
|
||||||
|
self.names.deinit();
|
||||||
|
self.allocator.free(self.connections);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn findPath(allocator: Allocator, used_edges: []bool, graph: ComponentGraph, start: ComponentId, end: ComponentId) !?std.ArrayList(ComponentId) {
|
||||||
|
const parent = try allocator.alloc(?ComponentId, graph.names.items.len);
|
||||||
|
defer allocator.free(parent);
|
||||||
|
@memset(parent, null);
|
||||||
|
|
||||||
|
var stack = std.ArrayList(ComponentId).init(allocator);
|
||||||
|
defer stack.deinit();
|
||||||
|
|
||||||
|
try stack.append(start);
|
||||||
|
parent[start] = start;
|
||||||
|
|
||||||
|
const node_count = graph.names.items.len;
|
||||||
|
while (stack.popOrNull()) |node_id| {
|
||||||
|
if (node_id == end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (graph.connections[node_id].constSlice()) |other_node_id| {
|
||||||
|
const visited_index = node_count * node_id + other_node_id;
|
||||||
|
if (parent[other_node_id] == null and !used_edges[visited_index]) {
|
||||||
|
parent[other_node_id] = node_id;
|
||||||
|
try stack.append(other_node_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent[end] == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = std.ArrayList(ComponentId).init(allocator);
|
||||||
|
errdefer result.deinit();
|
||||||
|
|
||||||
|
try result.append(end);
|
||||||
|
|
||||||
|
var current: ?ComponentId = end;
|
||||||
|
while (true) {
|
||||||
|
current = parent[current.?];
|
||||||
|
try result.insert(0, current.?);
|
||||||
|
if (current.? == start) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn countPaths(allocator: Allocator, graph: ComponentGraph, used_edges: []bool, from: ComponentId, to: ComponentId, max_paths: u32) !u32 {
|
||||||
|
const node_count = graph.names.items.len;
|
||||||
|
|
||||||
|
var path_count: u32 = 0;
|
||||||
|
while (path_count < max_paths) {
|
||||||
|
const maybe_path = try findPath(allocator, used_edges, graph, from, to);
|
||||||
|
if (maybe_path == null) break;
|
||||||
|
|
||||||
|
const path = maybe_path.?;
|
||||||
|
defer path.deinit();
|
||||||
|
|
||||||
|
for (0..(path.items.len-1)) |i| {
|
||||||
|
const segment_from = path.items[i];
|
||||||
|
const segment_to = path.items[i+1];
|
||||||
|
used_edges[segment_from * node_count + segment_to] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getIslandSize(allocator: Allocator, graph: ComponentGraph, used_edges: []bool, from: ComponentId) !usize {
|
||||||
|
const visisted = try allocator.alloc(bool, graph.names.items.len);
|
||||||
|
defer allocator.free(visisted);
|
||||||
|
@memset(visisted, false);
|
||||||
|
|
||||||
|
var stack = std.ArrayList(ComponentId).init(allocator);
|
||||||
|
defer stack.deinit();
|
||||||
|
|
||||||
|
try stack.append(from);
|
||||||
|
visisted[from] = true;
|
||||||
|
|
||||||
|
const node_count = graph.names.items.len;
|
||||||
|
while (stack.popOrNull()) |node_id| {
|
||||||
|
for (graph.connections[node_id].constSlice()) |other_node_id| {
|
||||||
|
const visited_index = node_count * node_id + other_node_id;
|
||||||
|
if (!visisted[other_node_id] and !used_edges[visited_index]) {
|
||||||
|
visisted[other_node_id] = true;
|
||||||
|
try stack.append(other_node_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std.mem.count(bool, visisted, &[_]bool{ true });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
|
const allocator = input.allocator;
|
||||||
|
const components = try parseInput(allocator, input.lines);
|
||||||
|
defer components.deinit();
|
||||||
|
|
||||||
|
const graph = try ComponentGraph.init(allocator, components.items);
|
||||||
|
defer graph.deinit();
|
||||||
|
|
||||||
|
// std.debug.print("Graph:\n", .{});
|
||||||
|
// for (0.., graph.connections) |node_id, connections| {
|
||||||
|
// std.debug.print("{s} ->", .{graph.getName(@intCast(node_id))});
|
||||||
|
// for (connections.constSlice()) |connection| {
|
||||||
|
// std.debug.print(" {s}", .{graph.getName(@intCast(connection))});
|
||||||
|
// }
|
||||||
|
// std.debug.print("\n", .{});
|
||||||
|
// }
|
||||||
|
|
||||||
|
var source_node: ?ComponentId = null;
|
||||||
|
var target_node: ?ComponentId = null;
|
||||||
|
|
||||||
|
const node_count = graph.names.items.len;
|
||||||
|
const used_edges = try allocator.alloc(bool, node_count * node_count);
|
||||||
|
defer allocator.free(used_edges);
|
||||||
|
|
||||||
|
for (0..(node_count-1)) |i| {
|
||||||
|
for ((i+1)..node_count) |j| {
|
||||||
|
@memset(used_edges, false);
|
||||||
|
const path_count = try countPaths(allocator, graph, used_edges, @intCast(i), @intCast(j), 4);
|
||||||
|
if (path_count == 3) {
|
||||||
|
source_node = @intCast(i);
|
||||||
|
target_node = @intCast(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source_node == null or target_node == null) return error.NoSolution;
|
||||||
|
|
||||||
|
const island_size1 = try getIslandSize(allocator, graph, used_edges, source_node.?);
|
||||||
|
const island_size2 = node_count - island_size1;
|
||||||
|
|
||||||
|
return .{ .uint = @intCast(island_size1 * island_size2) };
|
||||||
|
}
|
@ -44,6 +44,8 @@ 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")),
|
||||||
|
create_day(@import("./day25.zig")),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn kilobytes(count: u32) u32 {
|
fn kilobytes(count: u32) u32 {
|
||||||
@ -210,4 +212,6 @@ test {
|
|||||||
_ = @import("./day21.zig");
|
_ = @import("./day21.zig");
|
||||||
_ = @import("./day22.zig");
|
_ = @import("./day22.zig");
|
||||||
_ = @import("./day23.zig");
|
_ = @import("./day23.zig");
|
||||||
|
_ = @import("./day24.zig");
|
||||||
|
_ = @import("./day25.zig");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user