From c05d5587568c2fe251500ac29aebfcfa7538a31b Mon Sep 17 00:00:00 2001 From: John Costa Date: Sat, 27 Jul 2024 17:36:56 +0100 Subject: [PATCH] Year 2023 Day 16 complete --- AdventOfCode2023/src/day16/day16.zig | 441 +++++++++++++-------------- AdventOfCode2023/src/main.zig | 8 +- 2 files changed, 220 insertions(+), 229 deletions(-) diff --git a/AdventOfCode2023/src/day16/day16.zig b/AdventOfCode2023/src/day16/day16.zig index c77603e..0ef33cd 100644 --- a/AdventOfCode2023/src/day16/day16.zig +++ b/AdventOfCode2023/src/day16/day16.zig @@ -1,239 +1,230 @@ const std = @import("std"); const print = std.debug.print; +const Direction = enum(u2) { up = 0, right = 1, down = 2, left = 3 }; + +const VERTICAL_SPLITTER = '|'; +const HORIZONTAL_SPLITTER = '-'; + const EMPTY = '.'; -const VERTICAL_SPLIT = '|'; -const HORIZONTAL_SPLIT = '-'; -const CLOCKWISE_TURN = '/'; -const ANTI_CLOCKWISE_TURN = '\\'; +const RIGHT_MIRROR = '/'; -const Directions = enum { UP, DOWN, LEFT, RIGHT }; - -const BeamsController = struct { - beams: std.AutoHashMap(usize, *Beam), - allocator: std.mem.Allocator, - counter: usize, - heatmap: std.AutoHashMap(u128, bool), - splitsMade: std.AutoHashMap(u128, bool), - - pub fn walk(self: *BeamsController) !void { - var iter = self.*.beams.iterator(); - var deleteList = std.ArrayList(usize).init(self.allocator); - defer deleteList.deinit(); - - while (iter.next()) |entry| { - var delete = try entry.value_ptr.*.step(); - if (delete) { - // print("Beam {} removing\n", .{entry.value_ptr.*.index}); - try deleteList.append(entry.key_ptr.*); - // self.allocator.destroy(entry.value_ptr.*); - } else { - // var beams = entry.value_ptr.*; - // print("Beam {} ({},{}) {}\n", .{ beams.index, beams.x, beams.y, beams.direction }); - } - } - - for (deleteList.items) |i| { - _ = self.*.beams.remove(i); - } - } - - pub fn horizontalSplit(self: *BeamsController, beam: *Beam) !bool { - // Assume direction is up or down. - // Assume "parent" beam always goes right. - - var coord = @as(u128, beam.x) << 64 | @as(u128, beam.y); - if (self.*.splitsMade.get(coord)) |_| { - // dont split again - return true; - } - - var splitBeam = try self.allocator.create(Beam); - splitBeam.*.grid = beam.*.grid; - splitBeam.*.x = beam.*.x; - splitBeam.*.y = beam.*.y; - splitBeam.*.direction = Directions.LEFT; - splitBeam.*.controller = beam.*.controller; - - self.*.counter += 1; - splitBeam.*.index = self.*.counter; - - try self.*.beams.put(splitBeam.index, splitBeam); - try self.*.splitsMade.put(coord, true); - - return false; - } - - pub fn verticalSplit(self: *BeamsController, beam: *Beam) !bool { - // Assume direction is left or right - // Assume "parent" beam always goes down. - - var coord = @as(u128, beam.x) << 64 | @as(u128, beam.y); - if (self.*.splitsMade.get(coord)) |_| { - // dont split again - return true; - } - - var splitBeam = try self.allocator.create(Beam); - splitBeam.*.grid = beam.*.grid; - splitBeam.*.x = beam.*.x; - splitBeam.*.y = beam.*.y; - splitBeam.*.direction = Directions.UP; - splitBeam.*.controller = beam.*.controller; - - self.*.counter += 1; - splitBeam.*.index = self.*.counter; - - try self.*.beams.put(splitBeam.index, splitBeam); - try self.*.splitsMade.put(coord, true); - - return false; - } -}; +const LEFT_MIRROR = '\\'; const Beam = struct { - grid: *[][]const u8, x: usize, y: usize, - direction: Directions, - controller: *BeamsController, - index: usize, - pub fn step(self: *Beam) !bool { - var coord = @as(u128, self.x) << 64 | @as(u128, self.y); - try self.*.controller.heatmap.put(coord, true); - switch (self.direction) { - Directions.RIGHT => { - if (self.*.x == self.*.grid.*[0].len - 1) { - return true; - } + isActive: bool, - var tile = self.*.grid.*[self.*.y][self.*.x]; - if (tile == VERTICAL_SPLIT) { - // call beams controller vertical split. - var alreadyDone = try self.controller.verticalSplit(self); - if (alreadyDone) { - return true; - } - self.*.direction = Directions.DOWN; - } else if (tile == CLOCKWISE_TURN) { - // moving right. turn upwards. - self.*.direction = Directions.UP; - } else if (tile == ANTI_CLOCKWISE_TURN) { - self.*.direction = Directions.DOWN; - } - }, - Directions.LEFT => { - if (self.*.x == 0) { - return true; - } - - var tile = self.*.grid.*[self.*.y][self.*.x]; - if (tile == VERTICAL_SPLIT) { - // call beams controller vertical split. - var alreadyDone = try self.controller.verticalSplit(self); - if (alreadyDone) { - return true; - } - self.*.direction = Directions.DOWN; - } else if (tile == CLOCKWISE_TURN) { - // moving right. turn upwards. - self.*.direction = Directions.DOWN; - } else if (tile == ANTI_CLOCKWISE_TURN) { - self.*.direction = Directions.UP; - } - }, - Directions.UP => { - if (self.*.y == 0) { - return true; - } - - var tile = self.*.grid.*[self.*.y][self.*.x]; - if (tile == HORIZONTAL_SPLIT) { - // call beams controller vertical split. - var alreadyDone = try self.controller.horizontalSplit(self); - if (alreadyDone) { - return true; - } - self.*.direction = Directions.RIGHT; - } else if (tile == CLOCKWISE_TURN) { - // moving right. turn upwards. - self.*.direction = Directions.RIGHT; - } else if (tile == ANTI_CLOCKWISE_TURN) { - self.*.direction = Directions.LEFT; - } - }, - Directions.DOWN => { - if (self.*.y == self.*.grid.*.len - 1) { - return true; - } - - var tile = self.*.grid.*[self.*.y][self.*.x]; - if (tile == HORIZONTAL_SPLIT) { - // call beams controller vertical split. - var alreadyDone = try self.controller.horizontalSplit(self); - if (alreadyDone) { - return true; - } - self.*.direction = Directions.RIGHT; - } else if (tile == CLOCKWISE_TURN) { - // moving right. turn upwards. - self.*.direction = Directions.LEFT; - } else if (tile == ANTI_CLOCKWISE_TURN) { - self.*.direction = Directions.RIGHT; - } - }, - } - - switch (self.direction) { - Directions.UP => { - self.*.y -= 1; - }, - Directions.DOWN => { - self.*.y += 1; - }, - Directions.LEFT => { - self.*.x -= 1; - }, - Directions.RIGHT => { - self.*.x += 1; - }, - } - - coord = @as(u128, self.x) << 64 | @as(u128, self.y); - try self.*.controller.heatmap.put(coord, true); - - return false; - } + direction: Direction, }; -pub fn solve(input: [][]const u8) !void { - var allocator = std.heap.page_allocator; - var controller = BeamsController{ .allocator = allocator, .beams = std.AutoHashMap(usize, *Beam).init(allocator), .counter = 0, .heatmap = std.AutoHashMap(u128, bool).init(allocator), .splitsMade = std.AutoHashMap(u128, bool).init(allocator) }; - - var in = input; - - var firstBeam = try allocator.create(Beam); - firstBeam.*.x = 0; - firstBeam.*.y = 0; - firstBeam.*.direction = Directions.RIGHT; - firstBeam.*.grid = ∈ - firstBeam.*.controller = &controller; - firstBeam.*.index = 0; - - try controller.beams.put(0, firstBeam); - - while (controller.beams.count() > 0) { - try controller.walk(); - } - - var iter = controller.heatmap.iterator(); - while (iter.next()) |entry| { - var x = entry.key_ptr.* >> 64; - var y = entry.key_ptr.* & 0xFFFFFFFFFFFFFFFF; - print("{},{}\n", .{ x, y }); - } - - print("Part 1: {}\n", .{controller.heatmap.count()}); +fn encode_coord(x: usize, y: usize) u128 { + return @as(u128, x) << 64 | @as(u128, y); +} + +fn encode_coord_with_direction(x: usize, y: usize, direction: Direction) u130 { + return @as(u130, x) << 66 | @as(u130, y) << 2 | @intFromEnum(direction); +} + +fn tiles_covered(allocator: std.mem.Allocator, input: [][]const u8, initial_beam: *Beam) !usize { + var beams = std.ArrayList(*Beam).init(allocator); + + try beams.append(initial_beam); + + var tile_set = std.AutoHashMap(u128, bool).init(allocator); + try tile_set.put(encode_coord(initial_beam.x, initial_beam.y), true); + + var coord_direction_set = std.AutoHashMap(u130, bool).init(allocator); + + while (beams.items.len > 0) { + const owned_beams = try beams.toOwnedSlice(); + beams = std.ArrayList(*Beam).init(allocator); + + for (owned_beams) |beam| { + if (!beam.isActive) { + continue; + } + + const encoded_coord = encode_coord_with_direction(beam.x, beam.y, beam.direction); + + if (coord_direction_set.get(encoded_coord)) |_| { + continue; + } + + try coord_direction_set.put(encoded_coord, true); + try beams.append(beam); + + switch (input[beam.y][beam.x]) { + RIGHT_MIRROR => { + switch (beam.direction) { + Direction.up => beam.*.direction = Direction.right, + Direction.right => beam.*.direction = Direction.up, + Direction.down => beam.*.direction = Direction.left, + Direction.left => beam.*.direction = Direction.down, + } + }, + LEFT_MIRROR => { + switch (beam.direction) { + Direction.up => beam.*.direction = Direction.left, + Direction.right => beam.*.direction = Direction.down, + Direction.down => beam.*.direction = Direction.right, + Direction.left => beam.*.direction = Direction.up, + } + }, + VERTICAL_SPLITTER => { + switch (beam.direction) { + Direction.left, Direction.right => { + beam.*.direction = Direction.up; + + const new_beam = try allocator.create(Beam); + new_beam.*.x = beam.x; + new_beam.*.y = beam.y; + new_beam.*.isActive = true; + new_beam.*.direction = Direction.down; + + try beams.append(new_beam); + }, + else => {}, + } + }, + HORIZONTAL_SPLITTER => { + switch (beam.direction) { + Direction.up, Direction.down => { + beam.*.direction = Direction.right; + + const new_beam = try allocator.create(Beam); + new_beam.*.x = beam.x; + new_beam.*.y = beam.y; + new_beam.*.isActive = true; + new_beam.*.direction = Direction.left; + + try beams.append(new_beam); + }, + else => {}, + } + }, + else => {}, + } + + switch (beam.direction) { + Direction.up => { + if (beam.y == 0) { + beam.*.isActive = false; + continue; + } + + beam.*.y -= 1; + }, + Direction.right => { + if (beam.x == input[0].len - 1) { + beam.*.isActive = false; + continue; + } + + beam.*.x += 1; + }, + Direction.down => { + if (beam.y == input.len - 1) { + beam.*.isActive = false; + continue; + } + + beam.*.y += 1; + }, + Direction.left => { + if (beam.*.x == 0) { + beam.*.isActive = false; + continue; + } + + beam.*.x -= 1; + }, + } + + try tile_set.put(encode_coord(beam.x, beam.y), true); + } + } + + return tile_set.count(); +} + +pub fn solve(input: [][]const u8) !void { + const allocator = std.heap.page_allocator; + + var top_left_right = Beam{ .x = 0, .y = 0, .isActive = true, .direction = Direction.right }; + + const part1 = try tiles_covered(allocator, input, &top_left_right); + print("Part 1: {}\n", .{part1}); + + var starting_points = std.ArrayList(*Beam).init(allocator); + + var top_left_down = Beam{ .x = 0, .y = 0, .isActive = true, .direction = Direction.down }; + + var top_right_down = Beam{ .x = input[0].len - 1, .y = 0, .isActive = true, .direction = Direction.down }; + var top_right_left = Beam{ .x = input[0].len - 1, .y = 0, .isActive = true, .direction = Direction.left }; + + var bottom_left_right = Beam{ .x = 0, .y = input.len - 1, .isActive = true, .direction = Direction.right }; + var bottom_left_up = Beam{ .x = 0, .y = input.len - 1, .isActive = true, .direction = Direction.up }; + + var bottom_right_left = Beam{ .x = input[0].len - 1, .y = input.len - 1, .isActive = true, .direction = Direction.left }; + var bottom_right_up = Beam{ .x = input[0].len - 1, .y = input.len - 1, .isActive = true, .direction = Direction.up }; + + try starting_points.append(&top_left_right); + try starting_points.append(&top_left_down); + + try starting_points.append(&top_right_down); + try starting_points.append(&top_right_left); + + try starting_points.append(&bottom_left_right); + try starting_points.append(&bottom_left_up); + + try starting_points.append(&bottom_right_left); + try starting_points.append(&bottom_right_up); + + for (1..input.len) |y| { + var r = try allocator.create(Beam); + r.x = 0; + r.y = y; + r.isActive = true; + r.direction = Direction.right; + + var l = try allocator.create(Beam); + l.x = input[0].len - 1; + l.y = y; + l.isActive = true; + l.direction = Direction.left; + + try starting_points.append(l); + try starting_points.append(r); + } + + for (1..input[0].len) |x| { + var d = try allocator.create(Beam); + d.x = x; + d.y = 0; + d.isActive = true; + d.direction = Direction.down; + + var u = try allocator.create(Beam); + u.x = x; + u.y = input.len - 1; + u.isActive = true; + u.direction = Direction.up; + + try starting_points.append(d); + try starting_points.append(u); + } + + var highest: usize = 0; + for (try starting_points.toOwnedSlice()) |starting| { + const covered = try tiles_covered(allocator, input, starting); + + if (covered > highest) { + highest = covered; + } + } + + print("Part 2: {}\n", .{highest}); } -// 6858 diff --git a/AdventOfCode2023/src/main.zig b/AdventOfCode2023/src/main.zig index cb17460..6cf27d0 100644 --- a/AdventOfCode2023/src/main.zig +++ b/AdventOfCode2023/src/main.zig @@ -10,10 +10,10 @@ const std = @import("std"); // const day10 = @import("./day10/day10.zig"); // const day11 = @import("./day11/day11.zig"); // const day = @import("./day12/day12.zig"); -const day14 = @import("./day14/day14.zig"); +// const day14 = @import("./day14/day14.zig"); // const day13 = @import("./day13/day13.zig"); // const day15 = @import("./day15/day15.zig"); -// const day16 = @import("./day16/day16.zig"); +const day16 = @import("./day16/day16.zig"); // const day18 = @import("./day18/day18.zig"); // const day19 = @import("./day19/day19.zig"); // const day21 = @import("./day21/day21.zig"); @@ -23,8 +23,8 @@ const utils = @import("utils.zig"); pub fn main() !void { const allocator = std.heap.page_allocator; - const input = try utils.getInput("./src/day14/input.txt", allocator); + const input = try utils.getInput("./src/day16/input.txt", allocator); defer allocator.free(input); - try day14.solve(input); + try day16.solve(input); }