solve day 18 part 1
This commit is contained in:
parent
413e13b601
commit
692b178154
262
src/day18.zig
Normal file
262
src/day18.zig
Normal file
@ -0,0 +1,262 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const aoc = @import("./aoc.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const PointLib = @import("./point.zig");
|
||||
const PointI32 = PointLib.PointI32;
|
||||
const PointU32 = PointLib.PointU32;
|
||||
|
||||
const Direction = enum {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
|
||||
fn from_str(str: []const u8) ?Direction {
|
||||
if (std.mem.eql(u8, str, "U")) {
|
||||
return .Up;
|
||||
} else if (std.mem.eql(u8, str, "D")) {
|
||||
return .Down;
|
||||
} else if (std.mem.eql(u8, str, "L")) {
|
||||
return .Left;
|
||||
} else if (std.mem.eql(u8, str, "R")) {
|
||||
return .Right;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn to_offset(self: Direction) PointI32 {
|
||||
return switch (self) {
|
||||
.Up => PointI32{ .x = 0, .y = -1 },
|
||||
.Down => PointI32{ .x = 0, .y = 1 },
|
||||
.Left => PointI32{ .x = -1, .y = 0 },
|
||||
.Right => PointI32{ .x = 1, .y = 0 },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const DigInstruction = struct {
|
||||
dir: Direction,
|
||||
steps: u32
|
||||
};
|
||||
|
||||
const Instructions = std.ArrayList(DigInstruction);
|
||||
|
||||
fn parse_dig_instruction1(line: []const u8) !DigInstruction {
|
||||
var iter = std.mem.splitScalar(u8, line, ' ');
|
||||
|
||||
const dir_str = iter.next() orelse return error.MissingDirection;
|
||||
const steps_str = iter.next() orelse return error.MissingSteps;
|
||||
|
||||
return DigInstruction{
|
||||
.dir = Direction.from_str(dir_str) orelse return error.InvalidDirection,
|
||||
.steps = try std.fmt.parseUnsigned(u32, steps_str, 10)
|
||||
};
|
||||
}
|
||||
|
||||
fn parse_input1(allocator: Allocator, lines: []const []const u8) !Instructions {
|
||||
var result = Instructions.init(allocator);
|
||||
errdefer result.deinit();
|
||||
|
||||
for (lines) |line| {
|
||||
try result.append(try parse_dig_instruction1(line));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn get_bounds(insts: Instructions) std.meta.Tuple(&.{ PointI32, PointI32 }) {
|
||||
var upper_left = PointI32.zero();
|
||||
var lower_right = PointI32.zero();
|
||||
|
||||
var current = PointI32.zero();
|
||||
for (insts.items) |inst| {
|
||||
const step = inst.dir.to_offset().mul(@intCast(inst.steps));
|
||||
current = current.add(step);
|
||||
|
||||
upper_left.x = @min(upper_left.x, current.x);
|
||||
upper_left.y = @min(upper_left.y, current.y);
|
||||
lower_right.x = @max(lower_right.x, current.x);
|
||||
lower_right.y = @max(lower_right.y, current.y);
|
||||
}
|
||||
|
||||
return .{ upper_left, lower_right };
|
||||
}
|
||||
|
||||
fn flood_fill(allocator: Allocator, map: []bool, map_size: PointU32, from: PointU32) !void {
|
||||
var stack = std.ArrayList(PointU32).init(allocator);
|
||||
defer stack.deinit();
|
||||
|
||||
try stack.append(from);
|
||||
map[from.y * map_size.x + from.x] = true;
|
||||
|
||||
while (stack.popOrNull()) |point| {
|
||||
const directions = [_]Direction{
|
||||
.Up,
|
||||
.Right,
|
||||
.Down,
|
||||
.Left
|
||||
};
|
||||
for (directions) |dir| {
|
||||
const offset = dir.to_offset();
|
||||
const neighbour_x = @as(i32, @intCast(point.x)) + offset.x;
|
||||
const neighbour_y = @as(i32, @intCast(point.y)) + offset.y;
|
||||
|
||||
if (!(0 <= neighbour_x and neighbour_x < map_size.x)) continue;
|
||||
if (!(0 <= neighbour_y and neighbour_y < map_size.y)) continue;
|
||||
|
||||
const neighbour = PointU32{
|
||||
.x = @intCast(neighbour_x),
|
||||
.y = @intCast(neighbour_y)
|
||||
};
|
||||
const neighbour_idx = neighbour.y * map_size.x + neighbour.x;
|
||||
if (!map[neighbour_idx]) {
|
||||
map[neighbour_idx] = true;
|
||||
try stack.append(neighbour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_flood_fill_start(map: []bool, width: u32, height: u32) ?PointU32 {
|
||||
assert(height >= 2);
|
||||
|
||||
var found_wall = false;
|
||||
for (1..height) |y| {
|
||||
for (0..width) |x| {
|
||||
const idx = y * width + x;
|
||||
if (!found_wall) {
|
||||
if (map[idx]) {
|
||||
found_wall = true;
|
||||
}
|
||||
} else {
|
||||
if (!map[idx]) {
|
||||
return PointU32{ .x = @intCast(x), .y = @intCast(y) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn get_lagoon_size(allocator: Allocator, instructions: Instructions) !usize {
|
||||
const bounds = get_bounds(instructions);
|
||||
const upper_left: PointI32 = bounds[0];
|
||||
const lower_right: PointI32 = bounds[1];
|
||||
const size = PointU32{
|
||||
.x = @abs(lower_right.x - upper_left.x) + 1,
|
||||
.y = @abs(lower_right.y - upper_left.y) + 1
|
||||
};
|
||||
|
||||
std.debug.print("size: {any}\n", .{size});
|
||||
|
||||
const map = try allocator.alloc(bool, size.x * size.y);
|
||||
defer allocator.free(map);
|
||||
@memset(map, false);
|
||||
|
||||
var current = PointI32.zero();
|
||||
for (instructions.items) |inst| {
|
||||
const dir_offset = inst.dir.to_offset();
|
||||
|
||||
for (0..inst.steps) |i| {
|
||||
const pos = current.add(dir_offset.mul(@intCast(i)));
|
||||
const idx = (pos.y - upper_left.y) * @as(i32, @intCast(size.x)) + (pos.x - upper_left.x);
|
||||
map[@intCast(idx)] = true;
|
||||
}
|
||||
|
||||
const step = dir_offset.mul(@intCast(inst.steps));
|
||||
current = current.add(step);
|
||||
}
|
||||
|
||||
const flood_fill_start = find_flood_fill_start(map, size.x, size.y) orelse return error.NoFloodFillStart;
|
||||
try flood_fill(allocator, map, size, flood_fill_start);
|
||||
|
||||
// for (0..size.y) |y| {
|
||||
// for (0..size.x) |x| {
|
||||
// const idx = size.x * y + x;
|
||||
// if (map[idx]) {
|
||||
// std.debug.print("#", .{});
|
||||
// } else {
|
||||
// std.debug.print(".", .{});
|
||||
// }
|
||||
// }
|
||||
// std.debug.print("\n", .{});
|
||||
// }
|
||||
|
||||
return std.mem.count(bool, map, &.{ true });
|
||||
}
|
||||
|
||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||
const allocator = input.allocator;
|
||||
|
||||
var instructions = Instructions.init(allocator);
|
||||
defer instructions.deinit();
|
||||
for (input.lines) |line| {
|
||||
try instructions.append(try parse_dig_instruction1(line));
|
||||
}
|
||||
|
||||
return .{ .uint = try get_lagoon_size(allocator, instructions) };
|
||||
}
|
||||
|
||||
fn parse_dig_instruction2(line: []const u8) !DigInstruction {
|
||||
var iter = std.mem.splitScalar(u8, line, ' ');
|
||||
|
||||
_ = iter.next();
|
||||
_ = iter.next();
|
||||
|
||||
const color_str = iter.next() orelse return error.MissingColor;
|
||||
const color_trimmed = std.mem.trim(u8, color_str, "()#");
|
||||
if (color_trimmed.len != 6) return error.ColorTooShort;
|
||||
|
||||
const dir: Direction = switch (color_trimmed[5]) {
|
||||
'0' => .Up,
|
||||
'1' => .Down,
|
||||
'2' => .Left,
|
||||
'3' => .Up,
|
||||
else => return error.InvalidDirection
|
||||
};
|
||||
|
||||
return DigInstruction{
|
||||
.dir = dir,
|
||||
.steps = try std.fmt.parseUnsigned(u32, color_trimmed[1..5], 16)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||
const allocator = input.allocator;
|
||||
|
||||
var instructions = Instructions.init(allocator);
|
||||
defer instructions.deinit();
|
||||
for (input.lines) |line| {
|
||||
try instructions.append(try parse_dig_instruction2(line));
|
||||
}
|
||||
|
||||
return .{ .uint = try get_lagoon_size(allocator, instructions) };
|
||||
}
|
||||
|
||||
const example_input = [_][]const u8{
|
||||
"R 6 (#70c710)",
|
||||
"D 5 (#0dc571)",
|
||||
"L 2 (#5713f0)",
|
||||
"D 2 (#d2c081)",
|
||||
"R 2 (#59c680)",
|
||||
"D 2 (#411b91)",
|
||||
"L 5 (#8ceee2)",
|
||||
"U 2 (#caa173)",
|
||||
"L 1 (#1b58a2)",
|
||||
"U 2 (#caa171)",
|
||||
"R 2 (#7807d2)",
|
||||
"U 3 (#a77fa3)",
|
||||
"L 2 (#015232)",
|
||||
"U 2 (#7a21e3)",
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
try aoc.expectAnswerUInt(part1, 62, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
try aoc.expectAnswerUInt(part2, 952408144115, &example_input);
|
||||
}
|
@ -38,6 +38,7 @@ const Days = [_]aoc.Day{
|
||||
create_day(@import("./day15.zig")),
|
||||
create_day(@import("./day16.zig")),
|
||||
create_day(@import("./day17.zig")),
|
||||
create_day(@import("./day18.zig")),
|
||||
};
|
||||
|
||||
fn kilobytes(count: u32) u32 {
|
||||
@ -198,4 +199,5 @@ test {
|
||||
_ = @import("./day15.zig");
|
||||
_ = @import("./day16.zig");
|
||||
_ = @import("./day17.zig");
|
||||
_ = @import("./day18.zig");
|
||||
}
|
||||
|
@ -34,6 +34,13 @@ pub fn Point(comptime T: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn mul(self: Self, scale: T) Self {
|
||||
return Self{
|
||||
.x = self.x * scale,
|
||||
.y = self.y * scale
|
||||
};
|
||||
}
|
||||
|
||||
pub fn to_i32(self: Self) Point(i32) {
|
||||
return Point(i32){
|
||||
.x = @intCast(self.x),
|
||||
|
Loading…
Reference in New Issue
Block a user