solve day 16
This commit is contained in:
parent
ab6a4d4bb9
commit
11273d884e
@ -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;
|
||||||
|
@ -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
311
src/day16.zig
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user