Compare commits

...

2 Commits

Author SHA1 Message Date
14c4b266c1 solve day 18 part 2 2024-04-24 23:05:45 +03:00
692b178154 solve day 18 part 1 2024-04-24 21:43:55 +03:00
3 changed files with 225 additions and 0 deletions

216
src/day18.zig Normal file
View 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);
}

View File

@ -38,6 +38,7 @@ const Days = [_]aoc.Day{
create_day(@import("./day15.zig")), create_day(@import("./day15.zig")),
create_day(@import("./day16.zig")), create_day(@import("./day16.zig")),
create_day(@import("./day17.zig")), create_day(@import("./day17.zig")),
create_day(@import("./day18.zig")),
}; };
fn kilobytes(count: u32) u32 { fn kilobytes(count: u32) u32 {
@ -198,4 +199,5 @@ test {
_ = @import("./day15.zig"); _ = @import("./day15.zig");
_ = @import("./day16.zig"); _ = @import("./day16.zig");
_ = @import("./day17.zig"); _ = @import("./day17.zig");
_ = @import("./day18.zig");
} }

View File

@ -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) { pub fn to_i32(self: Self) Point(i32) {
return Point(i32){ return Point(i32){
.x = @intCast(self.x), .x = @intCast(self.x),