From 11273d884eabec4214e014fd40b050a53e170d70 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Mon, 15 Apr 2024 00:30:52 +0300 Subject: [PATCH] solve day 16 --- src/day10.zig | 2 +- src/day15.zig | 1 - src/day16.zig | 311 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/day3.zig | 2 +- src/main.zig | 2 + src/point.zig | 27 ++++- 6 files changed, 341 insertions(+), 4 deletions(-) create mode 100644 src/day16.zig diff --git a/src/day10.zig b/src/day10.zig index 3399bf6..1af0bf5 100644 --- a/src/day10.zig +++ b/src/day10.zig @@ -271,7 +271,7 @@ fn get_next_pipe(grid: *const Grid, x: u32, y: u32, current_path: []Point) ?Poin if (current_path.len > 1) { const first_path_point = current_path[0]; const prev_path_point = current_path[current_path.len-2]; - if (prev_path_point.eql(&point) or first_path_point.eql(&point)) continue; + if (prev_path_point.eql(point) or first_path_point.eql(point)) continue; } return point; diff --git a/src/day15.zig b/src/day15.zig index d05b25b..cceb790 100644 --- a/src/day15.zig +++ b/src/day15.zig @@ -1,6 +1,5 @@ const std = @import("std"); const aoc = @import("./aoc.zig"); -const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Input = struct { diff --git a/src/day16.zig b/src/day16.zig new file mode 100644 index 0000000..f55250a --- /dev/null +++ b/src/day16.zig @@ -0,0 +1,311 @@ +const std = @import("std"); +const aoc = @import("./aoc.zig"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; + +const Point = @import("./point.zig").Point; +const PointI32 = Point(i32); +const PointU32 = Point(u32); + +const UP = PointI32{ .x = 0, .y = -1 }; +const DOWN = PointI32{ .x = 0, .y = 1 }; +const LEFT = PointI32{ .x = -1, .y = 0 }; +const RIGHT = PointI32{ .x = 1, .y = 0 }; + +const Tile = enum { + Empty, + VerticalSplit, + HorizontalSplit, + LeftMirror, + RightMirror, + + fn from_u8(symbol: u8) ?Tile { + return switch (symbol) { + '.' => .Empty, + '\\' => .LeftMirror, + '/' => .RightMirror, + '-' => .HorizontalSplit, + '|' => .VerticalSplit, + else => null + }; + } + + fn to_u8(self: Tile) u8 { + return switch (self) { + .Empty => '.', + .LeftMirror => '\\', + .RightMirror => '/', + .HorizontalSplit => '-', + .VerticalSplit => '|', + }; + } +}; + +const Map = struct { + tiles: std.ArrayList(Tile), + width: u32, + height: u32, + + fn init(allocator: Allocator, width: u32, height: u32) !Map { + var tiles = std.ArrayList(Tile).init(allocator); + errdefer tiles.deinit(); + + try tiles.appendNTimes(.Empty, width * height); + + return Map{ + .tiles = tiles, + .width = width, + .height = height + }; + } + + fn get_idx(self: Map, x: u32, y: u32) usize { + assert(x < self.width); + assert(y < self.height); + return y * self.width + x; + } + + fn get(self: Map, x: u32, y: u32) Tile { + return self.tiles.items[self.get_idx(x, y)]; + } + + fn in_bounds(self: Map, x: i32, y: i32) bool { + return (0 <= x and x < self.width) and (0 <= y and y < self.height); + } + + fn deinit(self: Map) void { + self.tiles.deinit(); + } + + fn print(self: Map) void { + for (0..self.height) |y| { + for (0..self.width) |x| { + const tile = self.get(@intCast(x), @intCast(y)); + + std.debug.print("{c}", .{tile.to_u8()}); + } + std.debug.print("\n", .{}); + } + } +}; + +const Beam = struct { + pos: PointU32, + dir: PointI32, +}; + +fn parse_input(allocator: Allocator, lines: []const []const u8) !Map { + const width: u32 = @intCast(lines[0].len); + const height: u32 = @intCast(lines.len); + const map = try Map.init(allocator, width, height); + + for (0.., lines) |y, line| { + for (0.., line) |x, symbol| { + const tile: Tile = Tile.from_u8(symbol) orelse return error.InvalidTile; + const tile_idx = map.get_idx(@intCast(x), @intCast(y)); + map.tiles.items[tile_idx] = tile; + } + } + + return map; +} + +fn cast_beam(map: Map, from: PointU32, dir: PointI32) PointU32 { + var current: PointI32 = from.to_i32(); + while (true) { + const next_point = current.add(dir); + if (!map.in_bounds(next_point.x, next_point.y)) break; + current = next_point; + + const tile = map.get(@intCast(current.x), @intCast(current.y)); + if (tile == .Empty) continue; + if (tile == .VerticalSplit and (dir.eql(UP) or dir.eql(DOWN))) continue; + if (tile == .HorizontalSplit and (dir.eql(LEFT) or dir.eql(RIGHT))) continue; + + break; + } + + return current.to_u32(); +} + +fn contains_point(comptime T: type, list: []Point(T), point: Point(T)) bool { + for (list) |other| { + if (other.eql(point)) { + return true; + } + } + return false; +} + +fn count_energized_tiles(energy_map: []bool) u64 { + var result: u64 = 0; + for (energy_map) |energy| { + result += @intFromBool(energy); + } + return result; +} + +fn simulate_beam(allocator: Allocator, map: Map, from_pos: PointU32, from_dir: PointI32) ![]bool { + var used_splits = std.ArrayList(PointU32).init(allocator); + defer used_splits.deinit(); + + var beams = std.ArrayList(Beam).init(allocator); + defer beams.deinit(); + + var energy_map = try allocator.alloc(bool, map.width * map.height); + errdefer allocator.free(energy_map); + + try beams.append(Beam{ .pos = from_pos, .dir = from_dir }); + + var is_first = true; + while (beams.popOrNull()) |beam| { + var destination: PointU32 = undefined; + if (is_first and map.get(beam.pos.x, beam.pos.y) != .Empty) { + destination = beam.pos; + } else { + destination = cast_beam(map, beam.pos, beam.dir); + if (destination.eql(beam.pos)) continue; + } + is_first = false; + + var current: PointU32 = beam.pos; + energy_map[map.get_idx(current.x, current.y)] = true; + while (!current.eql(destination)) { + current.x = @intCast(@as(i32, @intCast(current.x)) + beam.dir.x); + current.y = @intCast(@as(i32, @intCast(current.y)) + beam.dir.y); + energy_map[map.get_idx(current.x, current.y)] = true; + } + + const tile = map.get(destination.x, destination.y); + if (tile == .LeftMirror) { + if (beam.dir.eql(RIGHT)) { + try beams.append(Beam{ .pos = destination, .dir = DOWN }); + } else if (beam.dir.eql(UP)) { + try beams.append(Beam{ .pos = destination, .dir = LEFT }); + } else if (beam.dir.eql(LEFT)) { + try beams.append(Beam{ .pos = destination, .dir = UP }); + } else if (beam.dir.eql(DOWN)) { + try beams.append(Beam{ .pos = destination, .dir = RIGHT }); + } else { + unreachable; + } + } else if (tile == .RightMirror) { + if (beam.dir.eql(RIGHT)) { + try beams.append(Beam{ .pos = destination, .dir = UP }); + } else if (beam.dir.eql(DOWN)) { + try beams.append(Beam{ .pos = destination, .dir = LEFT }); + } else if (beam.dir.eql(LEFT)) { + try beams.append(Beam{ .pos = destination, .dir = DOWN }); + } else if (beam.dir.eql(UP)) { + try beams.append(Beam{ .pos = destination, .dir = RIGHT }); + } else { + unreachable; + } + } else if (tile == .VerticalSplit) { + if (!contains_point(u32, used_splits.items, destination)) { + try beams.append(Beam{ .pos = destination, .dir = UP }); + try beams.append(Beam{ .pos = destination, .dir = DOWN }); + try used_splits.append(destination); + } + } else if (tile == .HorizontalSplit) { + if (!contains_point(u32, used_splits.items, destination)) { + try beams.append(Beam{ .pos = destination, .dir = LEFT }); + try beams.append(Beam{ .pos = destination, .dir = RIGHT }); + try used_splits.append(destination); + } + } + } + + return energy_map; +} + +fn print_energy_map(map: Map, energy_map: []bool) void { + for (0..map.height) |y| { + for (0..map.width) |x| { + const idx = y * map.width + x; + if (energy_map[idx]) { + std.debug.print("#", .{}); + } else { + std.debug.print(".", .{}); + } + } + std.debug.print("\n", .{}); + } +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + const map = try parse_input(input.allocator, input.lines); + defer map.deinit(); + + var energy_map = try simulate_beam(allocator, map, PointU32.zero(), RIGHT); + defer allocator.free(energy_map); + + // print_energy_map(map, energy_map); + + return .{ .uint = count_energized_tiles(energy_map) }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + const map = try parse_input(input.allocator, input.lines); + defer map.deinit(); + + var result: u64 = 0; + + for (0..map.height) |y| { + { + const from = PointU32{ .x = 0, .y = @intCast(y) }; + const energy_map = try simulate_beam(allocator, map, from, RIGHT); + defer allocator.free(energy_map); + result = @max(result, count_energized_tiles(energy_map)); + } + + { + const from = PointU32{ .x = map.width-1, .y = @intCast(y) }; + const energy_map = try simulate_beam(allocator, map, from, LEFT); + defer allocator.free(energy_map); + result = @max(result, count_energized_tiles(energy_map)); + } + } + + for (0..map.width) |x| { + { + const from = PointU32{ .x = @intCast(x), .y = 0 }; + const energy_map = try simulate_beam(allocator, map, from, DOWN); + defer allocator.free(energy_map); + result = @max(result, count_energized_tiles(energy_map)); + } + + { + const from = PointU32{ .x = @intCast(x), .y = map.height-1 }; + const energy_map = try simulate_beam(allocator, map, from, UP); + defer allocator.free(energy_map); + result = @max(result, count_energized_tiles(energy_map)); + } + } + + return .{ .uint = result }; +} + +const example_input = [_][]const u8{ + ".|...\\....", + "|.-.\\.....", + ".....|-...", + "........|.", + "..........", + ".........\\", + "..../.\\\\..", + ".-.-/..|..", + ".|....-|.\\", + "..//.|....", +}; + +test "part 1 example" { + try aoc.expectAnswerUInt(part1, 46, &example_input); +} + +test "part 2 example" { + try aoc.expectAnswerUInt(part2, 51, &example_input); +} + diff --git a/src/day3.zig b/src/day3.zig index 9a5ecfd..ae6aff9 100644 --- a/src/day3.zig +++ b/src/day3.zig @@ -23,7 +23,7 @@ fn find_number_end(line: []const u8, from: u32) u32 { fn append_unique(list: *PointList, point: PointU32) !void { for (list.items) |item| { - if (item.eql(&point)) { return; } + if (item.eql(point)) { return; } } try list.append(point); } diff --git a/src/main.zig b/src/main.zig index 07299b4..1a3a502 100644 --- a/src/main.zig +++ b/src/main.zig @@ -36,6 +36,7 @@ const Days = [_]aoc.Day{ create_day(@import("./day13.zig")), create_day(@import("./day14.zig")), create_day(@import("./day15.zig")), + create_day(@import("./day16.zig")), }; fn kilobytes(count: u32) u32 { @@ -193,4 +194,5 @@ test { _ = @import("./day13.zig"); _ = @import("./day14.zig"); _ = @import("./day15.zig"); + _ = @import("./day16.zig"); } diff --git a/src/point.zig b/src/point.zig index 1816b0a..294f323 100644 --- a/src/point.zig +++ b/src/point.zig @@ -5,8 +5,33 @@ pub fn Point(comptime T: type) type { x: T, y: T, - pub fn eql(self: *const Self, other: *const Self) bool { + pub fn eql(self: Self, other: Self) bool { return self.x == other.x and self.y == other.y; } + + pub fn zero() Self { + return Self{ .x = 0, .y = 0 }; + } + + pub fn add(self: Self, other: Self) Self { + return Self{ + .x = self.x + other.x, + .y = self.y + other.y, + }; + } + + pub fn to_i32(self: Self) Point(i32) { + return Point(i32){ + .x = @intCast(self.x), + .y = @intCast(self.y), + }; + } + + pub fn to_u32(self: Self) Point(u32) { + return Point(u32){ + .x = @intCast(self.x), + .y = @intCast(self.y), + }; + } }; }