diff --git a/src/day18.zig b/src/day18.zig index 51b5ab2..c9e3f5b 100644 --- a/src/day18.zig +++ b/src/day18.zig @@ -6,6 +6,11 @@ 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, @@ -66,126 +71,75 @@ fn parse_input1(allocator: Allocator, lines: []const []const u8) !Instructions { return result; } -fn get_bounds(insts: Instructions) std.meta.Tuple(&.{ PointI32, PointI32 }) { +fn get_bounds(segments: []const Segment) 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); + 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 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); +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 (instructions.items) |inst| { - const dir_offset = inst.dir.to_offset(); + for (insts.items) |inst| { + const step = inst.dir.to_offset().mul(@intCast(inst.steps)); + const next = current.add(step); - 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; - } + try segments.append(Segment{ .start = current, .stop = next }); - const step = dir_offset.mul(@intCast(inst.steps)); - current = current.add(step); + current = next; } - 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); + return segments; +} - // 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", .{}); - // } +// 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; - return std.mem.count(bool, map, &.{ true }); + 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 { @@ -211,7 +165,7 @@ fn parse_dig_instruction2(line: []const u8) !DigInstruction { if (color_trimmed.len != 6) return error.ColorTooShort; const dir: Direction = switch (color_trimmed[5]) { - '0' => .Up, + '0' => .Right, '1' => .Down, '2' => .Left, '3' => .Up, @@ -220,7 +174,7 @@ fn parse_dig_instruction2(line: []const u8) !DigInstruction { return DigInstruction{ .dir = dir, - .steps = try std.fmt.parseUnsigned(u32, color_trimmed[1..5], 16) + .steps = try std.fmt.parseUnsigned(u32, color_trimmed[0..5], 16) }; }