diff --git a/day16/main.zig b/day16/main.zig new file mode 100644 index 0000000..37a020d --- /dev/null +++ b/day16/main.zig @@ -0,0 +1,254 @@ +const std = @import("std"); + +const Dir = enum(usize) { up, right, down, left }; +const Coord = struct { row: usize, col: usize }; +const Beam = struct { dir: Dir, coord: Coord }; + +pub fn main() !void { + var content = @embedFile("data.txt"); + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + + var grid = std.ArrayList(std.ArrayList(u8)).init(allocator); + defer grid.deinit(); + + var lines = std.mem.split(u8, content, "\n"); + + while (lines.next()) |line| { + if (line.len < 1) { + break; + } + + var lineArrayList = std.ArrayList(u8).init(allocator); + + for (line) |char| { + try lineArrayList.append(char); + } + + try grid.append(lineArrayList); + } + + const energy = try energize(Beam{ .dir = Dir.right, .coord = Coord{ .row = 0, .col = 0 } }, grid); + + std.debug.print("Part 1: {d}\n", .{energy}); + + const height = grid.items.len; + const width = grid.items[0].items.len; + + var maxEnergy: usize = 0; + + for (0..height) |row| { + for ([_]usize{ 0, width - 1 }) |col| { + const dir = if (col == 0) Dir.right else Dir.left; + const start = Beam{ .dir = dir, .coord = Coord{ .row = row, .col = col } }; + const e = try energize(start, grid); + if (e > maxEnergy) { + maxEnergy = e; + } + } + } + + for (0..width) |col| { + for ([_]usize{ 0, height - 1 }) |row| { + const dir = if (row == 0) Dir.down else Dir.up; + const start = Beam{ .dir = dir, .coord = Coord{ .row = row, .col = col } }; + const e = try energize(start, grid); + if (e > maxEnergy) { + maxEnergy = e; + } + } + } + + std.debug.print("Part 2: {d}\n", .{maxEnergy}); +} + +fn energize(start: Beam, grid: std.ArrayList(std.ArrayList(u8))) !usize { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + + var beams = std.ArrayList(Beam).init(allocator); + defer beams.deinit(); + + // add the first beam + try beams.append(start); + + var energized = std.AutoHashMap(Coord, void).init(allocator); + defer energized.deinit(); + + var beamHistory = std.AutoHashMap(Beam, void).init(allocator); + defer beamHistory.deinit(); + + const height = grid.items.len; + const width = grid.items[0].items.len; + + while (true) { + // make 2 new lists: beams to add, and beams to remove. + // batch these while iterating, and do it all after + var beamsToAdd = std.ArrayList(Beam).init(allocator); + defer beamsToAdd.deinit(); + + var beamsToRemove = std.ArrayList(usize).init(allocator); + defer beamsToRemove.deinit(); + + if (beams.items.len == 0) { + break; + } + + for ((&beams).items, 0..) |*b, beamNumber| { + if (isOutOfGrid(b, grid)) { + try beamsToRemove.append(beamNumber); + continue; + } + + try energized.put(b.coord, {}); + + if (beamHistory.contains(b.*)) { + try beamsToRemove.append(beamNumber); + continue; + } + + try beamHistory.put(b.*, {}); + + const row = b.coord.row; + const col = b.coord.col; + + const value = grid.items[row].items[col]; + + const goStraight = value == '.' or (value == '|' and (b.dir == .up or b.dir == .down)) or + (value == '-' and (b.dir == .left or b.dir == .right)); + + if (goStraight) { + switch (b.dir) { + .up => if (row == 0) { + b.coord.row = height + 10; // put it out of the grid to get cleaned up + } else { + b.coord.row -= 1; + }, + .right => b.coord.col += 1, + .down => b.coord.row += 1, + .left => if (col == 0) { + b.coord.col = width + 10; // put it out of the grid to get cleaned up + } else { + b.coord.col -= 1; + }, + } + } else if (value == '|') { + if (row == 0) { + // if we are on the top row, change the beam to go down + b.dir = Dir.down; + b.coord.row += 1; + } else { + // otherwise, change beam direction to up and move it up one cell + b.dir = Dir.up; + b.coord.row -= 1; + if (row != height - 1) { + // and if we're not on the _bottom_ row, + // create a new beam going down, below this cell + const newBeam = Beam{ .dir = Dir.down, .coord = Coord{ .row = row + 1, .col = col } }; + try beamsToAdd.append(newBeam); + } + } + } else if (value == '-') { + if (col == 0) { + // if we are on the first col, change the beam to go right + b.dir = Dir.right; + b.coord.col += 1; + } else { + // otherwise, change beam direction to left and move it left one cell + b.dir = Dir.left; + b.coord.col -= 1; + if (col != width - 1) { + // and if we're not on the _last_ column, + // create a new beam going right, to the right this cell + const newBeam = Beam{ .dir = Dir.right, .coord = Coord{ .row = row, .col = col + 1 } }; + try beamsToAdd.append(newBeam); + } + } + } else if (value == '/') { + switch (b.dir) { + .up => { + b.dir = Dir.right; + b.coord.col += 1; + }, + .right => if (row == 0) { + b.coord.row = height + 10; // put it out of the grid to get cleaned up + } else { + b.dir = Dir.up; + b.coord.row -= 1; + }, + .down => if (col == 0) { + b.coord.row = height + 10; // put it out of the grid to get cleaned up + } else { + b.dir = Dir.left; + b.coord.col -= 1; + }, + .left => { + b.dir = Dir.down; + b.coord.row += 1; + }, + } + } else if (value == '\\') { + switch (b.dir) { + .up => if (col == 0) { + b.coord.col = width + 10; // put it out of the grid to get cleaned up + } else { + b.dir = Dir.left; + b.coord.col -= 1; + }, + .right => { + b.dir = Dir.down; + b.coord.row += 1; + }, + .down => { + b.dir = Dir.right; + b.coord.col += 1; + }, + .left => if (row == 0) { + b.coord.row = height + 10; // put it out of the grid to get cleaned up + } else { + b.dir = Dir.up; + b.coord.row -= 1; + }, + } + } + } + + // remove the beams in reverse order + const len = beamsToRemove.items.len; + for (0..len) |j| { + const idx = beamsToRemove.items[len - j - 1]; + _ = beams.orderedRemove(idx); + } + beamsToRemove.clearAndFree(); + + // add the new beams + for (beamsToAdd.items) |beam| { + try beams.append(beam); + } + beamsToAdd.clearAndFree(); + } + + return energized.count(); +} + +fn isOutOfGrid(beam: *Beam, grid: std.ArrayList(std.ArrayList(u8))) bool { + const row = beam.coord.row; + const col = beam.coord.col; + const height = grid.items.len; + const width = grid.items[0].items.len; + return row < 0 or row >= height or col < 0 or col >= width; +} + +fn printGrid(grid: std.ArrayList(std.ArrayList(u8)), energized: std.AutoHashMap(Coord, void)) void { + for (grid.items, 0..) |line, row| { + for (line.items, 0..) |char, col| { + if (char == '.' and energized.contains(Coord{ .row = row, .col = col })) { + std.debug.print("#", .{}); + } else { + std.debug.print("{c}", .{char}); + } + } + std.debug.print("\n", .{}); + } +}