116 lines
3.6 KiB
Zig
116 lines
3.6 KiB
Zig
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;
|
|
}
|