const std = @import("std"); pub const title = "Day 12: Christmas Tree Farm"; pub fn run(allocator: std.mem.Allocator) !void { const input = @embedFile("./input/day12.txt"); //const input = // \\0: // \\### // \\##. // \\##. // \\ // \\1: // \\### // \\##. // \\.## // \\ // \\2: // \\.## // \\### // \\##. // \\ // \\3: // \\##. // \\### // \\##. // \\ // \\4: // \\### // \\#.. // \\### // \\ // \\5: // \\### // \\.#. // \\### // \\ // \\4x4: 0 0 0 0 2 0 // \\12x5: 1 0 1 0 2 2 // \\12x5: 1 0 1 0 3 2 // ; var lines = std.mem.tokenizeScalar(u8, input, '\n'); var presents: std.ArrayList(Present) = .empty; defer presents.deinit(allocator); var regions: std.ArrayList(Region) = .empty; defer { for (regions.items) |region| { region.deinit(allocator); } regions.deinit(allocator); } while (lines.next()) |line| { var semicolon_pos: usize = 0; var x_pos: ?usize = null; for (line, 0..) |c, i| { switch (c) { 'x' => x_pos = i, ':' => { semicolon_pos = i; break; }, else => {}, } } if (semicolon_pos != 0) { if (x_pos) |p| { // region const width = try std.fmt.parseUnsigned(usize, line[0..p], 10); const height = try std.fmt.parseUnsigned(usize, line[p + 1..semicolon_pos], 10); var required_presents: std.ArrayList(u8) = .empty; defer required_presents.deinit(allocator); if (presents.items.len > 0) { try required_presents.ensureTotalCapacity(allocator, presents.items.len); } var presents_iter = std.mem.tokenizeScalar(u8, line[semicolon_pos + 2..], ' '); while (presents_iter.next()) |present| { const amount = try std.fmt.parseUnsigned(u8, present, 10); try required_presents.append(allocator, amount); } const required_presents_slice = try required_presents.toOwnedSlice(allocator); errdefer allocator.free(required_presents_slice); const region: Region = .{ .width = width, .height = height, .required_presents = required_presents_slice, }; try regions.append(allocator, region); } else { // present var present: Present = .{ .shape = .initEmpty(), }; for (0..Present.Width) |y| { const l = lines.next() orelse unreachable; for (0..Present.Width) |x| { if (l[x] == '#') { present.set(x, y); } } } try presents.append(allocator, present); } } } var accumulator: usize = 0; for (regions.items) |region| { std.debug.print("{f}\n", .{region}); var area_needed: usize = 0; for (region.required_presents, 0..) |amount, i| { area_needed += amount * presents.items[i].area(); } if (area_needed < region.area()) { std.debug.print("{f}\nfits\n", .{region}); accumulator += 1; } } var buffer: [8]u8 = undefined; var stdout_writer = std.fs.File.stdout().writer(&buffer); const stdout = &stdout_writer.interface; try stdout.print("{d}\n", .{accumulator}); try stdout.flush(); } const Present = struct { shape: std.bit_set.IntegerBitSet(Width * Width), const Width = 3; const Self = @This(); pub fn format(self: Self, w: *std.Io.Writer) std.Io.Writer.Error!void { for (0..Width) |y| { for (0..Width) |x| { try w.writeByte(if (self.isSet(x, y)) '#' else '.'); } if (y < Width - 1) { try w.writeByte('\n'); } } } pub fn set(self: *Self, x: usize, y: usize) void { const index = (y * Width) + x; self.shape.set(index); } pub fn isSet(self: Self, x: usize, y: usize) bool { const index = (y * Width) + x; return self.shape.isSet(index); } pub fn area(self: Self) usize { return self.shape.count(); } }; const Region = struct { width: usize, height: usize, required_presents: []u8, const Self = @This(); pub fn deinit(self: Self, allocator: std.mem.Allocator) void { allocator.free(self.required_presents); } pub fn format(self: Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.print("{d}x{d}: ", .{self.width, self.height}); for (self.required_presents, 0..) |present, i| { try w.print("{d}", .{present}); if (i < self.required_presents.len - 1) { try w.writeByte(' '); } } } pub fn createEmpty(self: Self, allocator: std.mem.Allocator) !EmptyRegion { return try .init(allocator, self.width, self.height); } pub fn area(self: Self) usize { return self.width * self.height; } }; const EmptyRegion = struct { grid: [][]bool, filled_spaces: usize, allocator: std.mem.Allocator, const Self = @This(); pub fn init(allocator: std.mem.Allocator, width: u8, height: u8) !Self { const h = try allocator.alloc([]bool, height); for (0..height) |i| { const w = try allocator.alloc(u8, width); @memset(w, false); h[i] = w; } return .{ .grid = h, .filled_spaces = 0, .allocator = allocator, }; } pub fn deinit(self: Self) void { for (self.grid) |row| { self.allocator.free(row); } self.allocator.free(self.grid); } pub fn fits(self: Self, present: Present) bool { std.debug.assert(self.grid.len > 0); const size = self.grid.len * self.grid[0].len; if ((size - self.filled_spaces) < present.shape.count()) { return false; } // TODO: Implementation return false; } };