Compare commits
2 Commits
413e13b601
...
14c4b266c1
Author | SHA1 | Date | |
---|---|---|---|
14c4b266c1 | |||
692b178154 |
216
src/day18.zig
Normal file
216
src/day18.zig
Normal file
@ -0,0 +1,216 @@
|
||||
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 Segment = struct {
|
||||
start: PointI32,
|
||||
stop: PointI32,
|
||||
};
|
||||
|
||||
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(segments: []const Segment) std.meta.Tuple(&.{ PointI32, PointI32 }) {
|
||||
var upper_left = PointI32.zero();
|
||||
var lower_right = PointI32.zero();
|
||||
|
||||
for (segments) |segment| {
|
||||
inline for (&.{ segment.start, segment.stop }) |p| {
|
||||
upper_left.x = @min(upper_left.x, p.x);
|
||||
upper_left.y = @min(upper_left.y, p.y);
|
||||
lower_right.x = @max(lower_right.x, p.x);
|
||||
lower_right.y = @max(lower_right.y, p.y);
|
||||
}
|
||||
}
|
||||
|
||||
return .{ upper_left, lower_right };
|
||||
}
|
||||
|
||||
fn get_segments(allocator: Allocator, insts: Instructions) !std.ArrayList(Segment) {
|
||||
var segments = std.ArrayList(Segment).init(allocator);
|
||||
errdefer segments.deinit();
|
||||
|
||||
var current = PointI32.zero();
|
||||
for (insts.items) |inst| {
|
||||
const step = inst.dir.to_offset().mul(@intCast(inst.steps));
|
||||
const next = current.add(step);
|
||||
|
||||
try segments.append(Segment{ .start = current, .stop = next });
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
// GOD DAMN IT, AGAIN WITH THIS THEOROM! I THOUGH IT WAS SCANLINES!
|
||||
// Shoelace theorum + Pick's theorum
|
||||
// Day 10 all over again :/
|
||||
fn get_lagoon_size(allocator: Allocator, instructions: Instructions) !usize {
|
||||
var boundry_points: u32 = 0;
|
||||
|
||||
var loop_path = std.ArrayList(PointI32).init(allocator);
|
||||
defer loop_path.deinit();
|
||||
|
||||
var current = PointI32.zero();
|
||||
try loop_path.append(current);
|
||||
for (instructions.items) |inst| {
|
||||
const step = inst.dir.to_offset().mul(@intCast(inst.steps));
|
||||
current = current.add(step);
|
||||
boundry_points += inst.steps;
|
||||
|
||||
try loop_path.append(current);
|
||||
}
|
||||
|
||||
var twice_area: i64 = 0;
|
||||
for (0..(loop_path.items.len-1)) |i| {
|
||||
const cur = loop_path.items[i];
|
||||
const next = loop_path.items[i+1];
|
||||
|
||||
const y_0: i64 = @intCast(cur.y);
|
||||
const x_0: i64 = @intCast(cur.x);
|
||||
const y_1: i64 = @intCast(next.y);
|
||||
const x_1: i64 = @intCast(next.x);
|
||||
|
||||
twice_area += (x_0 * y_1) - (y_0 * x_1);
|
||||
}
|
||||
|
||||
const iterior_points = @abs(twice_area) / 2 - boundry_points / 2 + 1;
|
||||
const result = iterior_points + boundry_points;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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' => .Right,
|
||||
'1' => .Down,
|
||||
'2' => .Left,
|
||||
'3' => .Up,
|
||||
else => return error.InvalidDirection
|
||||
};
|
||||
|
||||
return DigInstruction{
|
||||
.dir = dir,
|
||||
.steps = try std.fmt.parseUnsigned(u32, color_trimmed[0..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