const std = @import("std");
const Str = []const u8;
const PATH = "input/day13.txt";
pub fn first(allocator: std.mem.Allocator) !isize {
var tk = TableKnights{ .pairs = undefined };
tk.pairs = try parseInput(allocator, @embedFile(PATH));
defer tk.pairs.deinit();
tk.permute(0, tk.table.len);
return tk.max;
}
pub fn second(allocator: std.mem.Allocator) !isize {
var tk = TableKnights{ .pairs = undefined };
tk.pairs = try parseInput(allocator, @embedFile(PATH));
defer tk.pairs.deinit();
tk.permute(0, tk.table.len);
var min: isize = std.math.maxInt(isize);
// find the least good pair, replace that with myself (zeros)
var idx: usize = 0;
while (idx < tk.best.len) : (idx += 1) {
const val = tk.pairs.get(.{ tk.best[idx], tk.best[(idx + 1) % tk.best.len] }) orelse
tk.pairs.get(.{ tk.best[(idx + 1) % tk.best.len], tk.best[idx] }).?;
if (val < min) min = val;
}
return tk.max - min;
}
test "day13a" {
try std.testing.expectEqual(@as(isize, 709), try first(std.testing.allocator));
}
test "day13b" {
try std.testing.expectEqual(@as(isize, 668), try second(std.testing.allocator));
}
const Pairs = std.AutoHashMap([2]u8, isize);
const TableKnights = struct {
pairs: Pairs,
table: [8]u8 = .{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'M' },
best: [8]u8 = undefined,
max: isize = 0,
fn permute(self: *@This(), start: usize, end: usize) void {
if (start == end) {
const value = self.calValue();
if (value > self.max) {
self.max = value;
self.best = self.table;
}
} else {
var idx: usize = start;
while (idx < end) : (idx += 1) {
std.mem.swap(u8, &self.table[start], &self.table[idx]);
self.permute(start + 1, end);
std.mem.swap(u8, &self.table[start], &self.table[idx]);
}
}
}
fn calValue(self: @This()) isize {
var sum: isize = 0;
var idx: usize = 0;
while (idx < self.table.len) : (idx += 1) {
sum += self.pairs.get(.{ self.table[idx], self.table[(idx + 1) % self.table.len] }) orelse
self.pairs.get(.{ self.table[(idx + 1) % self.table.len], self.table[idx] }).?;
}
return sum;
}
};
fn parseInput(allocator: std.mem.Allocator, in: Str) !Pairs {
var pairs = Pairs.init(allocator);
var lines = std.mem.tokenize(u8, in, "\n");
while (lines.next()) |line| {
var words = std.mem.tokenize(u8, line, " ");
var pair: [2]u8 = undefined;
pair[0] = words.next().?[0];
_ = words.next(); // would
// lose of gain
const gain: isize = switch (words.next().?[0]) {
'g' => try std.fmt.parseUnsigned(isize, words.next().?, 10),
'l' => try std.fmt.parseUnsigned(isize, words.next().?, 10) * -1,
else => unreachable,
};
// drop "happiness units by sitting next to"
var idx: usize = 0;
while (idx < 6) : (idx += 1) {
_ = words.next();
}
pair[1] = words.next().?[0];
if (pairs.contains(pair)) {
pairs.getPtr(pair).?.* += gain;
} else if (pairs.contains(.{ pair[1], pair[0] })) {
pairs.getPtr(.{ pair[1], pair[0] }).?.* += gain;
} else {
try pairs.put(pair, gain);
}
}
std.debug.assert(pairs.count() == 8 * 7 / 2);
return pairs;
}