const std = @import("std");
const PATH = "input/day16.txt";
const Str = []const u8;
pub fn first(allocator: std.mem.Allocator) !usize {
var sues = SueList.init(allocator);
defer sues.deinit();
try parseInput(&sues, @embedFile(PATH));
std.debug.assert(sues.items.len == 500);
inline for (@typeInfo(Sue).Struct.fields) |fld| {
comptime if (std.mem.eql(u8, "id", fld.name)) continue;
var next_sues = SueList.init(allocator);
for (sues.items) |sue| {
if (@field(sue, fld.name)) |val| {
if (val == @field(target_sue, fld.name)) {
try next_sues.append(sue);
}
} else {
try next_sues.append(sue);
}
}
sues.deinit();
sues = next_sues;
}
std.debug.assert(sues.items.len == 1);
return sues.items[0].id;
}
pub fn second(allocator: std.mem.Allocator) !usize {
var sues = SueList.init(allocator);
defer sues.deinit();
try parseInput(&sues, @embedFile(PATH));
std.debug.assert(sues.items.len == 500);
inline for (@typeInfo(Sue).Struct.fields) |fld| {
comptime if (std.mem.eql(u8, "id", fld.name)) continue;
var next_sues = SueList.init(allocator);
for (sues.items) |sue| {
if (@field(sue, fld.name)) |val| {
if (std.mem.eql(u8, "cats", fld.name) or std.mem.eql(u8, "trees", fld.name)) {
if (val > @field(target_sue, fld.name).?) try next_sues.append(sue);
} else if (std.mem.eql(u8, "pomeranians", fld.name) or std.mem.eql(u8, "goldfish", fld.name)) {
if (val < @field(target_sue, fld.name).?) try next_sues.append(sue);
} else {
if (val == @field(target_sue, fld.name)) {
try next_sues.append(sue);
}
}
} else {
try next_sues.append(sue);
}
}
sues.deinit();
sues = next_sues;
}
std.debug.assert(sues.items.len == 1);
return sues.items[0].id;
}
test "day16a" {
try std.testing.expectEqual(@as(usize, 373), try first(std.testing.allocator));
}
test "day16b" {
try std.testing.expectEqual(@as(usize, 260), try second(std.testing.allocator));
}
const target_sue: Sue = .{
.id = 0,
.children = 3,
.cats = 7,
.samoyeds = 2,
.pomeranians = 3,
.akitas = 0,
.vizslas = 0,
.goldfish = 5,
.trees = 3,
.cars = 2,
.perfumes = 1,
};
const SueValue = u4;
const Sue = struct {
id: u9,
children: ?SueValue = null,
cats: ?SueValue = null,
samoyeds: ?SueValue = null,
pomeranians: ?SueValue = null,
akitas: ?SueValue = null,
vizslas: ?SueValue = null,
goldfish: ?SueValue = null,
trees: ?SueValue = null,
cars: ?SueValue = null,
perfumes: ?SueValue = null,
};
const SueList = std.ArrayList(Sue);
fn parseInput(sl: *SueList, input: Str) !void {
var lines = std.mem.tokenize(u8, input, "\n");
while (lines.next()) |line| {
var s: Sue = .{ .id = 0 };
const pivot = std.mem.indexOf(u8, line, ":").?;
s.id = try std.fmt.parseUnsigned(u9, line[4..pivot], 10);
var tags = std.mem.tokenize(u8, line[pivot + 2 ..], ", ");
while (tags.next()) |tag| {
// std.debug.print("{s}\n", .{tag});
switch (tag[0]) {
'a'...'z' => {
const val = try std.fmt.parseUnsigned(SueValue, tags.next().?, 10);
switch (tag[0]) {
'a' => s.akitas = val,
'c' => {
switch (tag[1]) {
'a' => {
switch (tag[2]) {
'r' => s.cars = val,
't' => s.cats = val,
else => unreachable,
}
},
'h' => s.children = val,
else => unreachable,
}
},
'g' => s.goldfish = val,
'p' => {
switch (tag[1]) {
'e' => s.perfumes = val,
'o' => s.pomeranians = val,
else => unreachable,
}
},
's' => s.samoyeds = val,
't' => s.trees = val,
'v' => s.vizslas = val,
else => unreachable,
}
},
else => unreachable,
}
}
try sl.append(s);
}
}