solve day 25

This commit is contained in:
Rokas Puzonas 2024-06-02 15:58:12 +03:00
parent d52e9bc73e
commit 4c30b52548
2 changed files with 264 additions and 0 deletions

262
src/day25.zig Normal file
View 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) };
}

View File

@ -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");
}