diff --git a/src/day16.zig b/src/day16.zig new file mode 100644 index 0000000..3ad469c --- /dev/null +++ b/src/day16.zig @@ -0,0 +1,159 @@ +const std = @import("std"); +const util = @import("util.zig"); +const mem = std.mem; + +pub fn main() !void { + const input = @embedFile("data/day16.txt"); + const sln = try solve(util.gpa, input); + std.debug.print("{d}\n", .{sln.a}); + std.debug.print("{d}\n", .{sln.b}); +} + +const Solution = struct { + a: usize, + b: usize, +}; + +const Direction = enum { + up, + right, + down, + left, + + fn x(self: @This()) isize { + return switch (self) { + .up, .down => 0, + .left => -1, + .right => 1, + }; + } + + fn y(self: @This()) isize { + return switch (self) { + .left, .right => 0, + .up => -1, + .down => 1, + }; + } + + fn rotate(self: @This(), nineties: usize) Direction { + return @enumFromInt((@intFromEnum(self) + nineties) % std.enums.values(@This()).len); + } +}; + +const Point = struct { + x: isize, + y: isize, + dir: Direction, +}; + +var states: std.AutoArrayHashMap(Point, void) = undefined; + +fn run(alloc: mem.Allocator, grid: [][]u8, visited: [][]bool, init_x: isize, init_y: isize, init_dir: Direction) !void { + var x = init_x; + var y = init_y; + var dir = init_dir; + + while (0 <= x and x < grid[0].len - 1 and 0 <= y and y < grid.len - 1) { + visited[@intCast(y)][@intCast(x)] = true; + var looped = try states.getOrPut(.{ .x = x, .y = y, .dir = dir }); + if (looped.found_existing) break; + // std.debug.print("-----\n", .{}); + // std.debug.print("{} {} {}\n", .{ x, y, dir }); + // std.time.sleep(1_000_000_000); + switch (grid[@intCast(y)][@intCast(x)]) { + '.' => { + x += dir.x(); + y += dir.y(); + }, + '|' => { + switch (dir) { + .up, .down => y += dir.y(), + .left, .right => { + try run(alloc, grid, visited, x, y - 1, .up); + try run(alloc, grid, visited, x, y + 1, .down); + }, + } + }, + '-' => { + switch (dir) { + .left, .right => x += dir.x(), + .up, .down => { + try run(alloc, grid, visited, x - 1, y, .left); + try run(alloc, grid, visited, x + 1, y, .right); + }, + } + }, + '/' => { + switch (dir) { + .left, .right => dir = dir.rotate(3), + .up, .down => dir = dir.rotate(1), + } + x += dir.x(); + y += dir.y(); + }, + '\\' => { + switch (dir) { + .left, .right => dir = dir.rotate(1), + .up, .down => dir = dir.rotate(3), + } + x += dir.x(); + y += dir.y(); + }, + else => unreachable, + } + } +} + +fn solve(alloc: mem.Allocator, comptime input: []const u8) !Solution { + const trimmed = mem.trimRight(u8, input, "\n\r"); + const w = mem.indexOfScalar(u8, trimmed, '\n') orelse unreachable; + const h = mem.count(u8, trimmed, "\n"); + + var visited = try util.allocGrid(bool, alloc, w, h, false); + const grid = try util.toGridOwned(alloc, trimmed, '\n'); + + states = std.AutoArrayHashMap(Point, void).init(alloc); + defer states.deinit(); + try run(alloc, grid, visited, 0, 0, .right); + + var energized: usize = 0; + for (visited) |row| { + for (row) |cell| { + if (cell) energized += 1; + } + } + + for (grid) |row| { + alloc.free(row); + } + for (visited) |row| { + alloc.free(row); + } + alloc.free(grid); + alloc.free(visited); + return .{ .a = energized, .b = 0 }; +} + +const sample = + \\.|...\.... + \\|.-.\..... + \\.....|-... + \\........|. + \\.......... + \\.........\ + \\..../.\\.. + \\.-.-/..|.. + \\.|....-|.\ + \\..//.|.... +; + +test "silver" { + const sln = try solve(std.testing.allocator, sample); + try std.testing.expectEqual(@as(usize, 46), sln.a); +} + +test "gold" { + // const sln = try solve(std.testing.allocator, input); + // try std.testing.expectEqual(@as(usize, 0), sln.b); +} diff --git a/src/util.zig b/src/util.zig index d262933..808a9ea 100644 --- a/src/util.zig +++ b/src/util.zig @@ -35,3 +35,31 @@ pub fn splitLines(buffer: []const u8) mem.SplitIterator(u8, .scalar) { pub fn splitSpace(buffer: []const u8) mem.SplitIterator(u8, .scalar) { return split(buffer, ' '); } + +pub fn allocGrid(comptime T: type, alloc: mem.Allocator, w: usize, h: usize, init: T) ![][]T { + var grid = try alloc.alloc([]T, h); + for (0..h) |i| { + grid[i] = try alloc.alloc(T, w); + @memset(grid[i], init); + } + return grid; +} + +pub fn toGridOwned(alloc: mem.Allocator, buffer: []const u8, sep: u8) ![][]u8 { + if (buffer.len == 0) return error.Empty; + const w = mem.indexOfScalar(u8, buffer, sep) orelse unreachable; + var h = mem.count(u8, buffer, &[_]u8{sep}); + if (buffer[buffer.len - 1] != sep) h += 1; + + var grid = try alloc.alloc([]u8, h); + + var it = split(buffer, sep); + var i: usize = 0; + while (it.next()) |line| { + grid[i] = try alloc.alloc(u8, w); + @memcpy(grid[i], line); + i += 1; + } + + return grid; +}