diff --git a/AdventOfCode2023/build.zig b/AdventOfCode2023/build.zig index e93d0fa..399b7c1 100644 --- a/AdventOfCode2023/build.zig +++ b/AdventOfCode2023/build.zig @@ -17,18 +17,11 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "AdventOfCode2023", - // In this case the main source file is merely a path, however, in more - // complicated build scripts, this could be a generated file. - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); - const utils_module = b.createModule(.{ - .source_file = .{ .path = "src/utils.zig" }, - }); - exe.addModule("utils", utils_module); - // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default // step when running `zig build`). @@ -60,7 +53,7 @@ pub fn build(b: *std.Build) void { // Creates a step for unit testing. This only builds the test executable // but does not run it. const unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); diff --git a/AdventOfCode2023/src/day23/day23.zig b/AdventOfCode2023/src/day23/day23.zig index 9fd69f6..002b2a0 100644 --- a/AdventOfCode2023/src/day23/day23.zig +++ b/AdventOfCode2023/src/day23/day23.zig @@ -43,22 +43,6 @@ pub fn Queue(comptime Child: type) type { }; } -test "Hash maps as refs" { - var allocator = std.heap.page_allocator; - - var q = Queue(*std.AutoHashMap(u128, bool)).init(allocator); - var m = std.AutoHashMap(u128, bool).init(allocator); - - try m.put(0, true); - try m.put(1, true); - try expect(m.count() == 2); - - try q.enqueue(&m); - var mPointer: *std.AutoHashMap(u128, bool) = q.dequeue().?; - - try expect(mPointer.*.count() == 2); -} - fn coord(myX: usize, myY: usize) u128 { return @as(u128, myX) << 64 | @as(u128, myY); } @@ -94,15 +78,6 @@ fn isStep(s: u8, want: u8) bool { return want == s; } -// const Edge = struct { -// weight: usize, -// node: *Node, -// }; -// -// const Node = struct { -// edges: []?Edge, -// }; - fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), root: u128, allocator: std.mem.Allocator, part2: bool) !usize { var q = Queue(u128).init(allocator); var qLength = Queue(usize).init(allocator); @@ -126,42 +101,41 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r var part1: usize = 0; while (q.dequeue()) |c| { - var child: u128 = c; - var size: usize = qLength.dequeue().?; - var paths: []u128 = qPaths.dequeue().?; + const child: u128 = c; + const size: usize = qLength.dequeue().?; + const paths: []u128 = qPaths.dequeue().?; if (child == goal) { - // don't return, because we need all routes. - print("Size: {}\n", .{size + 1}); part1 = size + 1; continue; } - var up = child - 1; - var upX = x(up); - var upY = y(up); + const up = child - 1; + const upX = x(up); + const upY = y(up); - var down = child + 1; - var downX = x(down); - var downY = y(down); + const down = child + 1; + const downX = x(down); + const downY = y(down); - var right = child + (@as(u128, 1) << 64); - var rightX = x(right); - var rightY = y(right); + const right = child + (@as(u128, 1) << 64); + const rightX = x(right); + const rightY = y(right); - var left = child - (@as(u128, 1) << 64); - var leftX = x(left); - var leftY = y(left); + const left = child - (@as(u128, 1) << 64); + const leftX = x(left); + const leftY = y(left); - var lastStep = paths[paths.len - 1]; - var lastStepX = x(lastStep); - var lastStepY = y(lastStep); + const lastStep = paths[paths.len - 1]; + const lastStepX = x(lastStep); + const lastStepY = y(lastStep); var l = grid[lastStepY][lastStepX]; + if (part2) { l = '.'; } - if (!linear_search(paths, up) and grid[upY][upX] != WALL and isStep(l, '^')) { + if (grid[upY][upX] != WALL and isStep(l, '^') and !linear_search(paths, up)) { var pathsCopy = try allocator.alloc(u128, paths.len + 1); short_copy(pathsCopy, paths); pathsCopy[pathsCopy.len - 1] = up; @@ -170,7 +144,8 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r try qLength.enqueue(size + 1); try qPaths.enqueue(pathsCopy); } - if (!linear_search(paths, down) and grid[downY][downX] != WALL and isStep(l, 'v')) { + + if (grid[downY][downX] != WALL and isStep(l, 'v') and !linear_search(paths, down)) { var pathsCopy = try allocator.alloc(u128, paths.len + 1); short_copy(pathsCopy, paths); pathsCopy[pathsCopy.len - 1] = down; @@ -179,7 +154,8 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r try qLength.enqueue(size + 1); try qPaths.enqueue(pathsCopy); } - if (!linear_search(paths, right) and grid[rightY][rightX] != WALL and isStep(l, '>')) { + + if (grid[rightY][rightX] != WALL and isStep(l, '>') and !linear_search(paths, right)) { var pathsCopy = try allocator.alloc(u128, paths.len + 1); short_copy(pathsCopy, paths); pathsCopy[pathsCopy.len - 1] = right; @@ -188,7 +164,8 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r try qLength.enqueue(size + 1); try qPaths.enqueue(pathsCopy); } - if (!linear_search(paths, left) and grid[leftY][leftX] != WALL and isStep(l, '<')) { + + if (grid[leftY][leftX] != WALL and isStep(l, '<') and !linear_search(paths, left)) { var pathsCopy = try allocator.alloc(u128, paths.len + 1); short_copy(pathsCopy, paths); pathsCopy[pathsCopy.len - 1] = left; @@ -204,65 +181,225 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r return part1; } -fn dps(grid: [][]const u8, root: u128, goal: u128, explored: []u128, size: usize, allocator: std.mem.Allocator) !usize { - if (root == goal) { - print("Size: {}\n", .{size}); - return size; +const Direction = enum { up, down, right, left }; + +fn num_of_non_wall(input: *const [][]const u8, coord1: u128, coord2: u128, coord3: u128, coord4: u128) u8 { + var counter: u8 = 0; + + if (input.*[y(coord1)][x(coord1)] != WALL) { + counter += 1; + } + if (input.*[y(coord2)][x(coord2)] != WALL) { + counter += 1; + } + if (input.*[y(coord3)][x(coord3)] != WALL) { + counter += 1; + } + if (input.*[y(coord4)][x(coord4)] != WALL) { + counter += 1; } - var up = root - 1; - var down = root + 1; - var right = root + (@as(u128, 1) << 64); - var left = root - (@as(u128, 1) << 64); - var neighbours = [_]u128{ up, down, right, left }; + return counter; +} - var highest: usize = 0; +fn direction(input: *const [][]const u8, explored: *std.AutoHashMap(u128, bool), up: u128, down: u128, right: u128, left: u128) Direction { + if (explored.get(up) == null and input.*[y(up)][x(up)] != WALL) { + return Direction.up; + } + if (explored.get(down) == null and input.*[y(down)][x(down)] != WALL) { + return Direction.down; + } + if (explored.get(left) == null and input.*[y(left)][x(left)] != WALL) { + return Direction.left; + } + if (explored.get(right) == null and input.*[y(right)][x(right)] != WALL) { + return Direction.right; + } - for (neighbours) |n| { - var nx = x(n); - var ny = y(n); - if (!linear_search(explored, n) and grid[ny][nx] != WALL) { - var copyExplored = try allocator.alloc(u128, explored.len + 1); - short_copy(copyExplored, explored); - copyExplored[copyExplored.len - 1] = n; - var h = try dps(grid, n, goal, copyExplored, size + 1, allocator); - if (h > highest) { - highest = h; + unreachable; +} + +const Edge = struct { distance: usize, to_x: usize, to_y: usize }; + +fn compress_graph(allocator: std.mem.Allocator, input: [][]const u8) !std.AutoHashMap(u128, []Edge) { + var nodes = std.ArrayList(u128).init(allocator); + defer nodes.deinit(); + + for (1..input.len - 1) |y_part| { + for (1..input[0].len - 1) |x_part| { + if (input[y_part][x_part] != '.') { + continue; } - allocator.free(copyExplored); + const up = coord(x_part, y_part - 1); + const down = coord(x_part, y_part + 1); + const right = coord(x_part + 1, y_part); + const left = coord(x_part - 1, y_part); + + const non_wall_count = num_of_non_wall(&input, up, down, right, left); + if (non_wall_count == 0 or non_wall_count == 1) { + unreachable; + } + + if (non_wall_count == 2) { + if (input[y(up)][x(up)] != WALL and input[y(down)][x(down)] != WALL) { + continue; + } + + if (input[y(left)][x(left)] != WALL and input[y(right)][x(right)] != WALL) { + continue; + } + } + + try nodes.append(coord(x_part, y_part)); } } - return highest; + var edges_map = std.AutoHashMap(u128, []Edge).init(allocator); + + for (nodes.items) |a| { + lower: for (nodes.items) |b| { + if (a == b) { + continue; + } + + if (x(a) == x(b)) { + // Check they can see each other. + 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 (input[counter_y][x(a)] == WALL) { + break true; + } + } else false; + + if (contains_walls) { + continue :lower; + } + + const new_edge = Edge{ .to_x = x(b), .to_y = y(b), .distance = max_y - min_y }; + + 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); + } + } + + 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; +} + +fn includes(nodes: []u128, node: u128) bool { + for (nodes) |n| { + if (n == node) { + return true; + } + } + + return false; +} + +fn dfs(allocator: std.mem.Allocator, edges_map: std.AutoHashMap(u128, []Edge), end: u128) !usize { + var stack = std.ArrayList(u128).init(allocator); + var stack_distance = std.ArrayList(usize).init(allocator); + var stack_explored = std.ArrayList([]u128).init(allocator); + + defer stack.deinit(); + defer stack_distance.deinit(); + defer stack_explored.deinit(); + + const initial_explored = try allocator.alloc(u128, 0); + + try stack.append(coord(1, 1)); + try stack_distance.append(2); + try stack_explored.append(initial_explored); + + var longest_distance: usize = 0; + + while (stack.items.len > 0) { + const popped_node = stack.pop(); + const popped_distance = stack_distance.pop(); + const popped_explored = stack_explored.pop(); + + defer allocator.free(popped_explored); + + if (includes(popped_explored, popped_node)) { + continue; + } + + if (popped_node == end and popped_distance > longest_distance) { + longest_distance = popped_distance; + } + + const edges = edges_map.get(popped_node).?; + for (edges) |edge| { + var copied_edges = try allocator.alloc(u128, popped_explored.len + 1); + std.mem.copyForwards(u128, copied_edges, popped_explored); + copied_edges[copied_edges.len - 1] = popped_node; + + try stack.append(coord(edge.to_x, edge.to_y)); + try stack_distance.append(popped_distance + edge.distance); + try stack_explored.append(copied_edges); + } + } + + return longest_distance; } pub fn solve(input: [][]const u8) !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - var allocator = gpa.allocator(); + const allocator = gpa.allocator(); var explored = std.AutoHashMap(u128, bool).init(allocator); - var start = coord(1, 0); + const start = coord(1, 0); try explored.put(start, true); - var fakeStart = coord(1, 1); + const goal = coord(input[0].len - 2, input.len - 2); - var goal = coord(input[0].len - 2, input.len - 1); - _ = goal; - - // var part1 = try bfs(input, goal, &explored, fakeStart, allocator, false); + // const part1 = try bfs(input, goal, &explored, fakeStart, allocator, false); // print("Part 1: {}\n", .{part1}); - // var part2 = try bfs(input, goal, &explored, fakeStart, allocator, true); - // print("Part 2: {}\n", .{part2}); - - var exp = try allocator.alloc(u128, 1); - exp[0] = start; - - // var exampleLastInter = coord(19, 20); - var exampleLastInter = coord(133, 137); - - var part2 = try dps(input, fakeStart, exampleLastInter, exp, 0, allocator); - print("Part 2: {}\n", .{part2 + 21}); + const edges = try compress_graph(allocator, input); + const part2 = try dfs(allocator, edges, goal); + print("Part 2: {}\n", .{part2}); } diff --git a/AdventOfCode2023/src/main.zig b/AdventOfCode2023/src/main.zig index af0565e..b21518b 100644 --- a/AdventOfCode2023/src/main.zig +++ b/AdventOfCode2023/src/main.zig @@ -19,15 +19,15 @@ const std = @import("std"); // const day19 = @import("./day19/day19.zig"); // const day20 = @import("./day20/day20.zig"); // const day21 = @import("./day21/day21.zig"); -const day22 = @import("./day22/day22.zig"); -// const day23 = @import("./day23/day23.zig"); +// const day22 = @import("./day22/day22.zig"); +const day23 = @import("./day23/day23.zig"); // const day24 = @import("./day24/day24.zig"); const utils = @import("utils.zig"); pub fn main() !void { const allocator = std.heap.page_allocator; - const input = try utils.getInput("./src/day22/input.txt", allocator); + const input = try utils.getInput("./src/day23/input.txt", allocator); defer allocator.free(input); - try day22.solve(input); + try day23.solve(input); }