diff --git a/AdventOfCode2023/src/day21/day21.zig b/AdventOfCode2023/src/day21/day21.zig index daae9bc..f74dca4 100644 --- a/AdventOfCode2023/src/day21/day21.zig +++ b/AdventOfCode2023/src/day21/day21.zig @@ -5,7 +5,7 @@ const ROCK = '#'; const START = 'S'; const GARDEN = '.'; const STEPS = 1; -const PART2_STEPS = 6; +const PART2_STEPS = 50; fn printGrid(grid: [][]u8) void { for (grid) |row| { @@ -154,6 +154,7 @@ pub fn solve(input: [][]const u8) !void { var eastCoordY = y(east) % input.len; if (grid[northCoordY][northCoordX] != ROCK) { + print("{} - {},{}\n", .{ north, northCoordX, northCoordY }); try newCurrentSteps.put(north, true); } if (grid[southCoordY][southCoordX] != ROCK) { diff --git a/AdventOfCode2023/src/day23/day23.zig b/AdventOfCode2023/src/day23/day23.zig new file mode 100644 index 0000000..9fd69f6 --- /dev/null +++ b/AdventOfCode2023/src/day23/day23.zig @@ -0,0 +1,268 @@ +const std = @import("std"); +const print = std.debug.print; +const expect = std.testing.expect; + +const WALL = '#'; + +pub fn Queue(comptime Child: type) type { + return struct { + const This = @This(); + const Node = struct { + data: Child, + next: ?*Node, + }; + gpa: std.mem.Allocator, + start: ?*Node, + end: ?*Node, + + pub fn init(gpa: std.mem.Allocator) This { + return This{ + .gpa = gpa, + .start = null, + .end = null, + }; + } + pub fn enqueue(this: *This, value: Child) !void { + const node = try this.gpa.create(Node); + node.* = .{ .data = value, .next = null }; + if (this.end) |end| end.next = node // + else this.start = node; + this.end = node; + } + pub fn dequeue(this: *This) ?Child { + const start = this.start orelse return null; + defer this.gpa.destroy(start); + if (start.next) |next| + this.start = next + else { + this.start = null; + this.end = null; + } + return start.data; + } + }; +} + +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); +} + +fn x(c: u128) usize { + return @truncate(c >> 64); +} + +fn y(c: u128) usize { + return @truncate(c); +} + +fn linear_search(arr: []u128, search: u128) bool { + for (arr) |i| { + if (i == search) { + return true; + } + } + return false; +} + +fn short_copy(longerArr: []u128, shorterArr: []u128) void { + for (0..shorterArr.len) |i| { + longerArr[i] = shorterArr[i]; + } +} + +fn isStep(s: u8, want: u8) bool { + if (s == '.') { + return true; + } + + 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); + var qPaths = Queue([]u128).init(allocator); + + try explored.*.put(root, true); + try q.enqueue(root); + try qLength.enqueue(0); + + var myPaths = try allocator.alloc(u128, 2); + + var keyIter = explored.keyIterator(); + var index: usize = 0; + while (keyIter.next()) |key| { + myPaths[index] = key.*; + index += 1; + } + + try qPaths.enqueue(myPaths); + + var part1: usize = 0; + + while (q.dequeue()) |c| { + var child: u128 = c; + var size: usize = qLength.dequeue().?; + var 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); + + var down = child + 1; + var downX = x(down); + var downY = y(down); + + var right = child + (@as(u128, 1) << 64); + var rightX = x(right); + var rightY = y(right); + + var left = child - (@as(u128, 1) << 64); + var leftX = x(left); + var leftY = y(left); + + var lastStep = paths[paths.len - 1]; + var lastStepX = x(lastStep); + var lastStepY = y(lastStep); + var l = grid[lastStepY][lastStepX]; + if (part2) { + l = '.'; + } + + if (!linear_search(paths, up) and grid[upY][upX] != WALL and isStep(l, '^')) { + var pathsCopy = try allocator.alloc(u128, paths.len + 1); + short_copy(pathsCopy, paths); + pathsCopy[pathsCopy.len - 1] = up; + + try q.enqueue(up); + try qLength.enqueue(size + 1); + try qPaths.enqueue(pathsCopy); + } + if (!linear_search(paths, down) and grid[downY][downX] != WALL and isStep(l, 'v')) { + var pathsCopy = try allocator.alloc(u128, paths.len + 1); + short_copy(pathsCopy, paths); + pathsCopy[pathsCopy.len - 1] = down; + + try q.enqueue(down); + try qLength.enqueue(size + 1); + try qPaths.enqueue(pathsCopy); + } + if (!linear_search(paths, right) and grid[rightY][rightX] != WALL and isStep(l, '>')) { + var pathsCopy = try allocator.alloc(u128, paths.len + 1); + short_copy(pathsCopy, paths); + pathsCopy[pathsCopy.len - 1] = right; + + try q.enqueue(right); + try qLength.enqueue(size + 1); + try qPaths.enqueue(pathsCopy); + } + if (!linear_search(paths, left) and grid[leftY][leftX] != WALL and isStep(l, '<')) { + var pathsCopy = try allocator.alloc(u128, paths.len + 1); + short_copy(pathsCopy, paths); + pathsCopy[pathsCopy.len - 1] = left; + + try q.enqueue(left); + try qLength.enqueue(size + 1); + try qPaths.enqueue(pathsCopy); + } + + allocator.free(paths); + } + + 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; + } + + 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 }; + + var highest: usize = 0; + + 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; + } + + allocator.free(copyExplored); + } + } + + return highest; +} + +pub fn solve(input: [][]const u8) !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + var allocator = gpa.allocator(); + + var explored = std.AutoHashMap(u128, bool).init(allocator); + + var start = coord(1, 0); + try explored.put(start, true); + + var fakeStart = coord(1, 1); + + var goal = coord(input[0].len - 2, input.len - 1); + _ = goal; + + // var 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}); +} diff --git a/AdventOfCode2023/src/day24/day24.zig b/AdventOfCode2023/src/day24/day24.zig new file mode 100644 index 0000000..7e53ef9 --- /dev/null +++ b/AdventOfCode2023/src/day24/day24.zig @@ -0,0 +1,111 @@ +const std = @import("std"); +const print = std.debug.print; +const expect = std.testing.expect; + +const Line = struct { + x: i128, + y: i128, + z: i128, + + dx: i128, + dy: i128, + dz: i128, + + pub fn isNegative(self: Line) bool { + if (self.dx < 0 and self.dy < 0) { + return false; + } + + return self.dx < 0 or self.dy < 0; + } + + pub fn time(self: Line, point: f128) f128 { + var fx: f128 = @floatFromInt(self.x); + var fdx: f128 = @floatFromInt(self.dx); + + return (point - fx) / fdx; + } +}; + +// const LOWER = 7; +// const HIGHER = 27; +const LOWER = 200000000000000; +const HIGHER = 400000000000000; + +const Solution = struct { + x: f128, + y: f128, + z: f128, +}; + +test "Floating points" { + var a: i128 = 5.123; + var b: i128 = 5.123; + + try expect(a - b == 0); +} + +fn asFloat(a: i128) f128 { + return @as(f128, @floatFromInt(a)); +} + +fn linear_algebra_2d(line1: Line, line2: Line) ?Solution { + var m1: f128 = asFloat(line1.dy) / asFloat(line1.dx); + var A: f128 = asFloat(line1.y) - m1 * asFloat(line1.x); + + var m2: f128 = asFloat(line2.dy) / asFloat(line2.dx); + var B: f128 = asFloat(line2.y) - m2 * asFloat(line2.x); + + if (m1 == m2) { + return null; + } + + var deltaM = m2 - m1; + + var x = A / deltaM - B / deltaM; + var y = (A * m2) / deltaM - (B * m1) / deltaM; + + return Solution{ .x = x, .y = y, .z = 0 }; +} + +pub fn solve(input: [][]const u8) !void { + var allocator = std.heap.page_allocator; + var lines = try allocator.alloc(Line, input.len); + + for (input, 0..) |line, i| { + var tokenizer = std.mem.tokenizeAny(u8, line, " ,@"); + var x = try std.fmt.parseInt(i128, tokenizer.next().?, 10); + var y = try std.fmt.parseInt(i128, tokenizer.next().?, 10); + var z = try std.fmt.parseInt(i128, tokenizer.next().?, 10); + + var dx = try std.fmt.parseInt(i128, tokenizer.next().?, 10); + var dy = try std.fmt.parseInt(i128, tokenizer.next().?, 10); + var dz = try std.fmt.parseInt(i128, tokenizer.next().?, 10); + + var l = Line{ .x = x, .y = y, .z = z, .dx = dx, .dy = dy, .dz = dz }; + lines[i] = l; + } + + var part1: usize = 0; + + for (0..lines.len) |i| { + for (i + 1..lines.len) |j| { + var line1 = lines[i]; + var line2 = lines[j]; + + var sol = linear_algebra_2d(line1, line2); + if (sol) |s| { + if (s.x > LOWER and s.y > LOWER and s.x < HIGHER and s.y < HIGHER) { + if (line1.time(s.x) < 0 or line2.time(s.x) < 0) { + continue; + } + + part1 += 1; + } + continue; + } + } + } + + print("Part 1: {}\n", .{part1}); +} diff --git a/AdventOfCode2023/src/main.zig b/AdventOfCode2023/src/main.zig index 5c70ce4..fb5edb8 100644 --- a/AdventOfCode2023/src/main.zig +++ b/AdventOfCode2023/src/main.zig @@ -14,13 +14,15 @@ const std = @import("std"); // const day16 = @import("./day16/day16.zig"); // const day18 = @import("./day18/day18.zig"); // const day19 = @import("./day19/day19.zig"); -const day21 = @import("./day21/day21.zig"); +// const day21 = @import("./day21/day21.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; - var input = try utils.getInput("./src/day21/input.txt", allocator); + var input = try utils.getInput("./src/day24/input.txt", allocator); defer allocator.free(input); - try day21.solve(input); + try day24.solve(input); }