solve day 16

This commit is contained in:
Rokas Puzonas 2024-04-15 00:30:52 +03:00
parent ab6a4d4bb9
commit 11273d884e
6 changed files with 341 additions and 4 deletions

View File

@ -271,7 +271,7 @@ fn get_next_pipe(grid: *const Grid, x: u32, y: u32, current_path: []Point) ?Poin
if (current_path.len > 1) { if (current_path.len > 1) {
const first_path_point = current_path[0]; const first_path_point = current_path[0];
const prev_path_point = current_path[current_path.len-2]; 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; return point;

View File

@ -1,6 +1,5 @@
const std = @import("std"); const std = @import("std");
const aoc = @import("./aoc.zig"); const aoc = @import("./aoc.zig");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Input = struct { const Input = struct {

311
src/day16.zig Normal file
View File

@ -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);
}

View File

@ -23,7 +23,7 @@ fn find_number_end(line: []const u8, from: u32) u32 {
fn append_unique(list: *PointList, point: PointU32) !void { fn append_unique(list: *PointList, point: PointU32) !void {
for (list.items) |item| { for (list.items) |item| {
if (item.eql(&point)) { return; } if (item.eql(point)) { return; }
} }
try list.append(point); try list.append(point);
} }

View File

@ -36,6 +36,7 @@ const Days = [_]aoc.Day{
create_day(@import("./day13.zig")), create_day(@import("./day13.zig")),
create_day(@import("./day14.zig")), create_day(@import("./day14.zig")),
create_day(@import("./day15.zig")), create_day(@import("./day15.zig")),
create_day(@import("./day16.zig")),
}; };
fn kilobytes(count: u32) u32 { fn kilobytes(count: u32) u32 {
@ -193,4 +194,5 @@ test {
_ = @import("./day13.zig"); _ = @import("./day13.zig");
_ = @import("./day14.zig"); _ = @import("./day14.zig");
_ = @import("./day15.zig"); _ = @import("./day15.zig");
_ = @import("./day16.zig");
} }

View File

@ -5,8 +5,33 @@ pub fn Point(comptime T: type) type {
x: T, x: T,
y: 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; 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),
};
}
}; };
} }