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; }