feat(day23): finally
This commit is contained in:
@ -2,6 +2,9 @@ const std = @import("std");
|
|||||||
const print = std.debug.print;
|
const print = std.debug.print;
|
||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Map = std.AutoHashMap;
|
||||||
|
|
||||||
const WALL = '#';
|
const WALL = '#';
|
||||||
|
|
||||||
pub fn Queue(comptime Child: type) type {
|
pub fn Queue(comptime Child: type) type {
|
||||||
@ -219,113 +222,188 @@ fn direction(input: *const [][]const u8, explored: *std.AutoHashMap(u128, bool),
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Edge = struct { distance: usize, to_x: usize, to_y: usize };
|
fn print_coord(c: u128, before: []const u8) void {
|
||||||
|
print("{s}{},{}\n", .{ before, y(c) + 1, x(c) + 1 });
|
||||||
|
}
|
||||||
|
|
||||||
fn compress_graph(allocator: std.mem.Allocator, input: [][]const u8) !std.AutoHashMap(u128, []Edge) {
|
fn arraylist_contains(list: *std.ArrayList(u128), node: u128) bool {
|
||||||
var nodes = std.ArrayList(u128).init(allocator);
|
for (list.items) |item| {
|
||||||
|
if (item == node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Edge = struct { distance: usize, to_x: usize, to_y: usize };
|
||||||
|
const State = struct {
|
||||||
|
node: u128,
|
||||||
|
distance: usize,
|
||||||
|
previous_node: u128,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn is_explored(explored: ArrayList(u128), c: u128) bool {
|
||||||
|
for (explored.items) |item| {
|
||||||
|
if (item == c) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const XY = struct { x: i64, y: i64 };
|
||||||
|
|
||||||
|
const directions: [4]XY = .{
|
||||||
|
XY{ .x = 0, .y = -1 }, // up
|
||||||
|
XY{ .x = 0, .y = 1 }, // down
|
||||||
|
XY{ .x = -1, .y = 0 }, // left
|
||||||
|
XY{ .x = 1, .y = 0 }, // right
|
||||||
|
};
|
||||||
|
|
||||||
|
fn adjacent_steps(input: *const [][]const u8, c: u128) usize {
|
||||||
|
var count: usize = 0;
|
||||||
|
|
||||||
|
const signed_x: i64 = @intCast(x(c));
|
||||||
|
const signed_y: i64 = @intCast(y(c));
|
||||||
|
|
||||||
|
inline for (directions) |d| {
|
||||||
|
const new_x: usize = @intCast(signed_x + d.x);
|
||||||
|
const new_y: usize = @intCast(signed_y + d.y);
|
||||||
|
|
||||||
|
if (input.*[new_y][new_x] != WALL) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_arraylist(allocator: std.mem.Allocator, source: ArrayList(u128)) !ArrayList(u128) {
|
||||||
|
var new_list = ArrayList(u128).init(allocator);
|
||||||
|
|
||||||
|
for (source.items) |item| {
|
||||||
|
try new_list.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// DFS over the uncompressed graph, but without a target node, so our
|
||||||
|
/// code runs over every part of the graph.
|
||||||
|
///
|
||||||
|
/// When you meet a co-ordinate you've been to, you must be at a intersection,
|
||||||
|
/// and can create a node, and add an edge.
|
||||||
|
///
|
||||||
|
fn compress_graph_v2(allocator: std.mem.Allocator, start: u128, goal: u128, exclude: u128, input: [][]const u8) !Map(u128, []Edge) {
|
||||||
|
var nodes = Map(u128, ArrayList(Edge)).init(allocator);
|
||||||
|
try nodes.put(start, ArrayList(Edge).init(allocator));
|
||||||
defer nodes.deinit();
|
defer nodes.deinit();
|
||||||
|
|
||||||
for (1..input.len - 1) |y_part| {
|
var stack = ArrayList(State).init(allocator);
|
||||||
for (1..input[0].len - 1) |x_part| {
|
defer stack.deinit();
|
||||||
if (input[y_part][x_part] != '.') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const up = coord(x_part, y_part - 1);
|
try stack.append(State{
|
||||||
const down = coord(x_part, y_part + 1);
|
.node = start,
|
||||||
const right = coord(x_part + 1, y_part);
|
.previous_node = start,
|
||||||
const left = coord(x_part - 1, y_part);
|
.distance = 0,
|
||||||
|
});
|
||||||
|
|
||||||
const non_wall_count = num_of_non_wall(&input, up, down, right, left);
|
var explored = ArrayList(u128).init(allocator);
|
||||||
if (non_wall_count == 0 or non_wall_count == 1) {
|
defer explored.deinit();
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (non_wall_count == 2) {
|
while (stack.popOrNull()) |state| {
|
||||||
if (input[y(up)][x(up)] != WALL and input[y(down)][x(down)] != WALL) {
|
const node = state.node;
|
||||||
continue;
|
var distance = state.distance;
|
||||||
}
|
var previous_node = state.previous_node;
|
||||||
|
|
||||||
if (input[y(left)][x(left)] != WALL and input[y(right)][x(right)] != WALL) {
|
if (node == exclude or node == coord(1, 0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try nodes.append(coord(x_part, y_part));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var edges_map = std.AutoHashMap(u128, []Edge).init(allocator);
|
// print("Exploring: {},{}\n", .{ y(node) + 1, x(node) + 1 });
|
||||||
|
|
||||||
for (nodes.items) |a| {
|
const node_x: i64 = @intCast(x(node));
|
||||||
lower: for (nodes.items) |b| {
|
const node_y: i64 = @intCast(y(node));
|
||||||
if (a == b) {
|
|
||||||
continue;
|
if ((adjacent_steps(&input, node) >= 3 and node != previous_node) or node == goal) {
|
||||||
|
// print("Found new graph node! {},{} | Prev: {},{}\n", .{ y(node) + 1, x(node) + 1, y(previous_node) + 1, x(previous_node) + 1 });
|
||||||
|
var edges = nodes.get(previous_node).?;
|
||||||
|
try edges.append(Edge{
|
||||||
|
.to_x = @intCast(node_x),
|
||||||
|
.to_y = @intCast(node_y),
|
||||||
|
.distance = distance,
|
||||||
|
});
|
||||||
|
try nodes.put(previous_node, edges);
|
||||||
|
|
||||||
|
if (nodes.get(node) == null) {
|
||||||
|
try nodes.put(node, ArrayList(Edge).init(allocator));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x(a) == x(b)) {
|
distance = 0;
|
||||||
// Check they can see each other.
|
previous_node = node;
|
||||||
const min_y = if (y(a) < y(b)) y(a) else y(b);
|
}
|
||||||
const max_y = if (y(a) > y(b)) y(a) else y(b);
|
|
||||||
|
|
||||||
const contains_walls = for (min_y..max_y) |counter_y| {
|
if (is_explored(explored, node)) {
|
||||||
if (input[counter_y][x(a)] == WALL) {
|
continue;
|
||||||
break true;
|
}
|
||||||
}
|
|
||||||
} else false;
|
|
||||||
|
|
||||||
if (contains_walls) {
|
try explored.append(node);
|
||||||
continue :lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
const new_edge = Edge{ .to_x = x(b), .to_y = y(b), .distance = max_y - min_y };
|
inline for (directions) |d| {
|
||||||
|
const new_x: usize = @intCast(node_x + d.x);
|
||||||
|
const new_y: usize = @intCast(node_y + d.y);
|
||||||
|
const new_coord = coord(new_x, new_y);
|
||||||
|
|
||||||
if (edges_map.get(a)) |edges_arr| {
|
if (input[new_y][new_x] != WALL) {
|
||||||
var new_edges_arr = try allocator.realloc(edges_arr, edges_arr.len + 1);
|
try stack.append(State{
|
||||||
new_edges_arr[new_edges_arr.len - 1] = new_edge;
|
.distance = distance + 1,
|
||||||
|
.node = new_coord,
|
||||||
try edges_map.put(a, new_edges_arr);
|
.previous_node = previous_node,
|
||||||
} else {
|
});
|
||||||
var new_edges_arr = try allocator.alloc(Edge, 1);
|
|
||||||
new_edges_arr[0] = new_edge;
|
|
||||||
|
|
||||||
try edges_map.put(a, new_edges_arr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y(a) == y(b)) {
|
|
||||||
// Check they can see each other.
|
|
||||||
const min_x = if (x(a) < x(b)) x(a) else x(b);
|
|
||||||
const max_x = if (x(a) > x(b)) x(a) else x(b);
|
|
||||||
|
|
||||||
const contains_walls = for (min_x..max_x) |counter_x| {
|
|
||||||
if (input[y(a)][counter_x] == WALL) {
|
|
||||||
break true;
|
|
||||||
}
|
|
||||||
} else false;
|
|
||||||
|
|
||||||
if (contains_walls) {
|
|
||||||
continue :lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
const new_edge = Edge{ .to_x = x(b), .to_y = y(b), .distance = max_x - min_x };
|
|
||||||
|
|
||||||
if (edges_map.get(a)) |edges_arr| {
|
|
||||||
var new_edges_arr = try allocator.realloc(edges_arr, edges_arr.len + 1);
|
|
||||||
new_edges_arr[new_edges_arr.len - 1] = new_edge;
|
|
||||||
|
|
||||||
try edges_map.put(a, new_edges_arr);
|
|
||||||
} else {
|
|
||||||
var new_edges_arr = try allocator.alloc(Edge, 1);
|
|
||||||
new_edges_arr[0] = new_edge;
|
|
||||||
|
|
||||||
try edges_map.put(a, new_edges_arr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return edges_map;
|
var solid_nodes = Map(u128, []Edge).init(allocator);
|
||||||
|
|
||||||
|
var nodes_iterator = nodes.iterator();
|
||||||
|
|
||||||
|
while (nodes_iterator.next()) |entry| {
|
||||||
|
const node = entry.key_ptr.*;
|
||||||
|
const edges = entry.value_ptr.*;
|
||||||
|
|
||||||
|
var expanded_edges = ArrayList(Edge).init(allocator);
|
||||||
|
for (edges.items) |edge| {
|
||||||
|
try expanded_edges.append(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inner_iterator = nodes.iterator();
|
||||||
|
while (inner_iterator.next()) |inner_entry| {
|
||||||
|
const inner_node = inner_entry.key_ptr.*;
|
||||||
|
const inner_edges = inner_entry.value_ptr.*;
|
||||||
|
|
||||||
|
if (inner_node == node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (inner_edges.items) |edge| {
|
||||||
|
if (coord(edge.to_x, edge.to_y) == node) {
|
||||||
|
try expanded_edges.append(Edge{
|
||||||
|
.to_x = x(inner_node),
|
||||||
|
.to_y = y(inner_node),
|
||||||
|
.distance = edge.distance,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try solid_nodes.put(node, try expanded_edges.toOwnedSlice());
|
||||||
|
}
|
||||||
|
|
||||||
|
return solid_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn includes(nodes: []u128, node: u128) bool {
|
fn includes(nodes: []u128, node: u128) bool {
|
||||||
@ -362,16 +440,20 @@ fn dfs(allocator: std.mem.Allocator, edges_map: std.AutoHashMap(u128, []Edge), e
|
|||||||
|
|
||||||
defer allocator.free(popped_explored);
|
defer allocator.free(popped_explored);
|
||||||
|
|
||||||
if (includes(popped_explored, popped_node)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (popped_node == end and popped_distance > longest_distance) {
|
if (popped_node == end and popped_distance > longest_distance) {
|
||||||
longest_distance = popped_distance;
|
longest_distance = popped_distance;
|
||||||
|
print("Longest so far: {}\n", .{longest_distance});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// print("{},{}\n", .{ y(popped_node) + 1, x(popped_node) + 1 });
|
||||||
|
// print("Distance: {}\n", .{popped_distance});
|
||||||
|
|
||||||
const edges = edges_map.get(popped_node).?;
|
const edges = edges_map.get(popped_node).?;
|
||||||
for (edges) |edge| {
|
for (edges) |edge| {
|
||||||
|
if (includes(popped_explored, coord(edge.to_x, edge.to_y))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var copied_edges = try allocator.alloc(u128, popped_explored.len + 1);
|
var copied_edges = try allocator.alloc(u128, popped_explored.len + 1);
|
||||||
std.mem.copyForwards(u128, copied_edges, popped_explored);
|
std.mem.copyForwards(u128, copied_edges, popped_explored);
|
||||||
copied_edges[copied_edges.len - 1] = popped_node;
|
copied_edges[copied_edges.len - 1] = popped_node;
|
||||||
@ -391,15 +473,28 @@ pub fn solve(input: [][]const u8) !void {
|
|||||||
|
|
||||||
var explored = std.AutoHashMap(u128, bool).init(allocator);
|
var explored = std.AutoHashMap(u128, bool).init(allocator);
|
||||||
|
|
||||||
const start = coord(1, 0);
|
const start = coord(1, 1);
|
||||||
try explored.put(start, true);
|
try explored.put(start, true);
|
||||||
|
|
||||||
const goal = coord(input[0].len - 2, input.len - 2);
|
const goal = coord(input[0].len - 2, input.len - 2);
|
||||||
|
const real_goal = coord(input[0].len - 2, input.len - 1);
|
||||||
|
|
||||||
// const part1 = try bfs(input, goal, &explored, fakeStart, allocator, false);
|
// const part1 = try bfs(input, goal, &explored, fakeStart, allocator, false);
|
||||||
// print("Part 1: {}\n", .{part1});
|
// print("Part 1: {}\n", .{part1});
|
||||||
|
|
||||||
const edges = try compress_graph(allocator, input);
|
var edges = try compress_graph_v2(allocator, start, goal, real_goal, input);
|
||||||
|
defer edges.deinit();
|
||||||
|
|
||||||
|
//var edges_iter = edges.iterator();
|
||||||
|
//while (edges_iter.next()) |entry| {
|
||||||
|
//print("({},{})\n", .{ y(entry.key_ptr.*) + 1, x(entry.key_ptr.*) + 1 });
|
||||||
|
|
||||||
|
//for (entry.value_ptr.*) |edge| {
|
||||||
|
//print(" -> ({},{}) | {}\n", .{ edge.to_y + 1, edge.to_x + 1, edge.distance });
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
print("Starting longest path\n", .{});
|
||||||
const part2 = try dfs(allocator, edges, goal);
|
const part2 = try dfs(allocator, edges, goal);
|
||||||
print("Part 2: {}\n", .{part2});
|
print("Part 2: {}\n", .{part2});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user