solve day 25
This commit is contained in:
parent
d52e9bc73e
commit
4c30b52548
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) };
|
||||
}
|
@ -45,6 +45,7 @@ const Days = [_]aoc.Day{
|
||||
create_day(@import("./day22.zig")),
|
||||
create_day(@import("./day23.zig")),
|
||||
create_day(@import("./day24.zig")),
|
||||
create_day(@import("./day25.zig")),
|
||||
};
|
||||
|
||||
fn kilobytes(count: u32) u32 {
|
||||
@ -212,4 +213,5 @@ test {
|
||||
_ = @import("./day22.zig");
|
||||
_ = @import("./day23.zig");
|
||||
_ = @import("./day24.zig");
|
||||
_ = @import("./day25.zig");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user