const std = @import("std"); // Wanted to implement both in a single modular and expansible way, so this is that. const string = []const u8; // to simplify a bit const Iterator = std.mem.SplitIterator(u8); const stepFnType = *const fn (*Iterator, []string) ?[]string; pub fn main() void { const input = @embedFile("input"); // The fundamental difference lies in the way of processing the file, // so we abstract that into a function to be able to pass its pointer. std.debug.print("Part 1: {}\n", .{sumPriorities(input, stepInline, 2)}); std.debug.print("Part 2: {}\n", .{sumPriorities(input, stepInterline, 3)}); } fn sumPriorities(input: string, stepFn: stepFnType, comptime amount: usize) usize { var totalPriorities: usize = 0; var iter = std.mem.split(u8, input, "\n"); // This buffer is needed to not allocate but it shouldn't be used directly var _buffer = [_]string{undefined} ** amount; // Use the stepper to divide into slices properly while (stepFn(&iter, &_buffer)) |containers| { // If it wasn't found just contine with the next line const repe = findCommon(u8, containers) orelse continue; // Convert to priority as specified // 'a-z' -> '1-26', 'A-Z' -> '27-52' const priority = if (repe >= 'a') repe - 'a' + 1 else repe - 'A' + 27; totalPriorities += priority; } return totalPriorities; } // Helper to find the common needle in n haystacks // Returns null if not found fn findCommon(comptime T: type, haystacks: [][]const T) ?T { // Why if (haystacks.len <= 1) unreachable; // We need to find a common needle for all of the haystacks. // Compare each of the characters in the first one with // the other ones in sequence. // // - If a match was found, jump to the next haystack. // - If a match wasn't found, just to the next needle. needle: for (haystacks[0]) |needle| { // Semaphor to know if something was found when reaching // the end of loops. var found: bool = false; haystack: for (haystacks[1..]) |haystack| { found = false; for (haystack) |blade| { if (needle == blade) { found = true; break; } } if (!found) { // We didn't find the needle, try with another one continue :needle; } else { // We did find it! // Let's see if it's in the next haystack continue :haystack; } } // We found the needle in all haystacks, hooray! if (found) return needle; } // We ran out of needles and nothing was found return null; } // Steps the iterator and slices lines in the specified amount fn stepInline(iter: *Iterator, buffer: []string) ?[]string { // We reached the end const line = iter.next() orelse return null; // Line isn't valid if (line.len == 0 or line.len % buffer.len != 0) return stepInline(iter, buffer); const len = line.len / buffer.len; for (buffer) |_, i| { // Split line into slices into buffer buffer[i] = line[i * len .. (i + 1) * len]; } return buffer; } // Steps the iterator and puts whole lines in the specified amount fn stepInterline(iter: *Iterator, buffer: []string) ?[]string { for (buffer) |_, i| { // No more lines const line = iter.next() orelse return null; // Invalid input, try next one if (line.len <= 0) return stepInterline(iter, buffer); buffer[i] = line; } return buffer; }