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("test.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", .{}); } }