const std = @import("std");
const PATH = "input/day15.txt";
const Str = []const u8;
const TEASPOON = 100;
pub fn first(allocator: std.mem.Allocator) !usize {
const ings = try parseInput(allocator, @embedFile(PATH));
defer allocator.free(ings);
var max: usize = 0;
var p: isize = 1;
while (p < TEASPOON) : (p += 1) {
var q: isize = 1;
while (q < TEASPOON - p) : (q += 1) {
var r: isize = 1;
while (r < TEASPOON - p - q) : (r += 1) {
var s: isize = 1;
while (s < TEASPOON - p - q - r + 1) : (s += 1) {
if (p + q + r + s != 100) continue;
const capacity = std.math.max(0, ings[0].capacity * p +
ings[1].capacity * q +
ings[2].capacity * r +
ings[3].capacity * s);
if (capacity == 0) continue;
const durability = std.math.max(0, ings[0].durability * p +
ings[1].durability * q +
ings[2].durability * r +
ings[3].durability * s);
if (durability == 0) continue;
const flavor = std.math.max(0, ings[0].flavor * p +
ings[1].flavor * q +
ings[2].flavor * r +
ings[3].flavor * s);
if (flavor == 0) continue;
const texture = std.math.max(0, ings[0].texture * p +
ings[1].texture * q +
ings[2].texture * r +
ings[3].texture * s);
if (texture == 0) continue;
const val = capacity * durability * flavor * texture;
if (val > max) max = @intCast(usize, val);
}
}
}
}
return max;
}
pub fn second(allocator: std.mem.Allocator) !usize {
const TARGET_CALORIES = 500;
const ings = try parseInput(allocator, @embedFile(PATH));
defer allocator.free(ings);
var max: usize = 0;
var p: isize = 1;
while (p < TEASPOON) : (p += 1) {
var q: isize = 1;
while (q < TEASPOON - p) : (q += 1) {
var r: isize = 1;
while (r < TEASPOON - p - q) : (r += 1) {
var s: isize = 1;
while (s < TEASPOON - p - q - r + 1) : (s += 1) {
if (p + q + r + s != 100) continue;
const calories =
ings[0].calories * p +
ings[1].calories * q +
ings[2].calories * r +
ings[3].calories * s;
if (calories != TARGET_CALORIES) continue;
const capacity = std.math.max(0, ings[0].capacity * p +
ings[1].capacity * q +
ings[2].capacity * r +
ings[3].capacity * s);
if (capacity == 0) continue;
const durability = std.math.max(0, ings[0].durability * p +
ings[1].durability * q +
ings[2].durability * r +
ings[3].durability * s);
if (durability == 0) continue;
const flavor = std.math.max(0, ings[0].flavor * p +
ings[1].flavor * q +
ings[2].flavor * r +
ings[3].flavor * s);
if (flavor == 0) continue;
const texture = std.math.max(0, ings[0].texture * p +
ings[1].texture * q +
ings[2].texture * r +
ings[3].texture * s);
if (texture == 0) continue;
const val = capacity * durability * flavor * texture;
if (val > max) max = @intCast(usize, val);
}
}
}
}
return max;
}
test "day15a" {
try std.testing.expectEqual(@as(usize, 222870), try first(std.testing.allocator));
}
test "day15b" {
try std.testing.expectEqual(@as(usize, 117936), try second(std.testing.allocator));
}
const Ingredient = struct {
capacity: isize,
durability: isize,
flavor: isize,
texture: isize,
calories: isize,
};
fn parseInput(allocator: std.mem.Allocator, input: Str) ![]Ingredient {
var ingredients = std.ArrayList(Ingredient).init(allocator);
defer ingredients.deinit();
var lines = std.mem.tokenize(u8, input, "\n");
while (lines.next()) |line| {
var name_prop = std.mem.tokenize(u8, line, ":");
_ = name_prop.next(); // drop name
var props = std.mem.tokenize(u8, name_prop.next().?, ",");
var ingr: Ingredient = undefined;
var counter: usize = 0;
while (props.next()) |prop| {
var name_val = std.mem.tokenize(u8, prop, " ");
_ = name_val.next();
const val = try std.fmt.parseInt(isize, name_val.next().?, 10);
switch (counter) {
0 => ingr.capacity = val,
1 => ingr.durability = val,
2 => ingr.flavor = val,
3 => ingr.texture = val,
4 => ingr.calories = val,
else => unreachable,
}
counter += 1;
}
try ingredients.append(ingr);
}
return try ingredients.toOwnedSlice();
}