diff --git a/src/day11.zig b/src/day11.zig new file mode 100644 index 0000000..572e35d --- /dev/null +++ b/src/day11.zig @@ -0,0 +1,217 @@ +const std = @import("std"); +const aoc = @import("./aoc.zig"); +const Allocator = std.mem.Allocator; +const Point = @import("./point.zig").Point(u32); +const ArrayList = std.ArrayList; + +const Tile = enum { + Empty, + Galaxy, + + fn to_char(self: Tile) u8 { + return switch (self) { + .Empty => '.', + .Galaxy => '#', + }; + } + + fn from_char(char: u8) ?Tile { + return switch (char) { + '.' => .Empty, + '#' => .Galaxy, + else => null + }; + } +}; + +const GalaxyMap = struct { + allocator: Allocator, + width: u32, + height: u32, + tiles: []Tile, + + fn deinit(self: GalaxyMap) void { + self.allocator.free(self.tiles); + } + + fn print(self: GalaxyMap) void { + for (0..self.height) |y| { + for (0..self.width) |x| { + const idx = y * self.width + x; + std.debug.print("{c}", .{self.tiles[idx].to_char()}); + } + std.debug.print("\n", .{}); + } + } +}; + +fn parse_input(allocator: Allocator, lines: []const []const u8) !GalaxyMap { + var height: u32 = @intCast(lines.len); + var width: u32 = @intCast(lines[0].len); + var tile_map = try allocator.alloc(Tile, width * height); + errdefer allocator.free(tile_map); + + for (0.., lines) |y, line| { + for (0.., line) |x, char| { + const idx = width * y + x; + tile_map[idx] = Tile.from_char(char) orelse .Empty; + } + } + + return GalaxyMap{ + .allocator = allocator, + .width = width, + .height = height, + .tiles = tile_map + }; +} + +fn list_galaxies(allocator: Allocator, map: GalaxyMap) !ArrayList(Point) { + var points = ArrayList(Point).init(allocator); + errdefer points.deinit(); + + for (0..map.height) |y| { + for (0..map.width) |x| { + const idx = y * map.width + x; + if (map.tiles[idx] != .Galaxy) continue; + + try points.append(Point{ + .x = @intCast(x), + .y = @intCast(y) + }); + } + } + + return points; +} + +fn is_row_empty(map: GalaxyMap, y: u32) bool { + for (0..map.width) |x| { + const idx = map.width * y + x; + if (map.tiles[idx] != .Empty) return false; + } + return true; +} + +fn is_column_empty(map: GalaxyMap, x: u32) bool { + for (0..map.height) |y| { + const idx = map.width * y + x; + if (map.tiles[idx] != .Empty) return false; + } + return true; +} + +fn list_empty_rows(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) { + var rows = ArrayList(u32).init(allocator); + errdefer rows.deinit(); + + for (0..map.height) |y| { + if (is_row_empty(map, @intCast(y))) { + try rows.append(@intCast(y)); + } + } + + return rows; +} + +fn list_empty_columns(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) { + var columns = ArrayList(u32).init(allocator); + errdefer columns.deinit(); + + for (0..map.width) |x| { + if (is_column_empty(map, @intCast(x))) { + try columns.append(@intCast(x)); + } + } + + return columns; +} + +fn expand_points(allocator: Allocator, points: []Point, map: GalaxyMap, amount: u32) !void { + const empty_rows = try list_empty_rows(allocator, map); + defer empty_rows.deinit(); + + const empty_columns = try list_empty_columns(allocator, map); + defer empty_columns.deinit(); + + for (points) |*p| { + var expand_by: u32 = 0; + for (empty_rows.items) |y| { + if (y < p.y) { + expand_by += 1; + } + } + p.y += expand_by*amount; + } + + for (points) |*p| { + var expand_by: u32 = 0; + for (empty_columns.items) |x| { + if (x < p.x) { + expand_by += 1; + } + } + p.x += amount*expand_by; + } +} + +fn sum_min_distances(points: []Point) u64 { + var result: u64 = 0; + for (0..(points.len-1)) |i| { + const p1 = points[i]; + for ((i+1)..points.len) |j| { + const p2 = points[j]; + const dx = @as(i64, @intCast(p1.x)) - @as(i64, @intCast(p2.x)); + const dy = @as(i64, @intCast(p1.y)) - @as(i64, @intCast(p2.y)); + result += std.math.absCast(dx) + std.math.absCast(dy); + } + } + return result; +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + const parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + var galaxies = try list_galaxies(allocator, parsed); + defer galaxies.deinit(); + + try expand_points(allocator, galaxies.items, parsed, 1); + + return .{ .uint = sum_min_distances(galaxies.items) }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + const parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + var galaxies = try list_galaxies(allocator, parsed); + defer galaxies.deinit(); + + try expand_points(allocator, galaxies.items, parsed, 1000000 - 1); + + return .{ .uint = sum_min_distances(galaxies.items) }; +} + +const example_input = [_][]const u8{ + "...#......", + ".......#..", + "#.........", + "..........", + "......#...", + ".#........", + ".........#", + "..........", + ".......#..", + "#...#....." +}; + +test "part 1 example" { + try aoc.expectAnswerUInt(part1, 374, &example_input); +} + +test "part 2 example" { + try aoc.expectAnswerUInt(part2, 82000210, &example_input); +} diff --git a/src/main.zig b/src/main.zig index eb2434a..9884386 100644 --- a/src/main.zig +++ b/src/main.zig @@ -31,6 +31,7 @@ const Days = [_]aoc.Day{ create_day(@import("day8.zig")), create_day(@import("day9.zig")), create_day(@import("day10.zig")), + create_day(@import("day11.zig")), }; fn kilobytes(count: u32) u32 { @@ -185,4 +186,5 @@ test { _ = @import("day8.zig"); _ = @import("day9.zig"); _ = @import("day10.zig"); + _ = @import("day11.zig"); }