const std = @import("std");
const Str = []const u8;
const PATH = "input/day07.txt";
pub fn first(allocator: std.mem.Allocator) !usize {
var known = std.StringHashMap(usize).init(allocator);
defer known.deinit();
return try resolve(&known, @embedFile(PATH));
}
pub fn second(allocator: std.mem.Allocator) !usize {
var known = std.StringHashMap(usize).init(allocator);
defer known.deinit();
try known.put("b", 956);
return try resolve(&known, @embedFile(PATH));
}
fn resolve(known: *std.StringHashMap(usize), input: Str) !usize {
var lines = std.mem.tokenize(u8, input, "\n");
while (true) {
const size = known.count();
lines.reset();
while (lines.next()) |line| {
var words = std.mem.tokenize(u8, line, " ");
if (std.mem.containsAtLeast(u8, line, 1, "NOT")) {
_ = words.next(); // NOT
const wire = words.next().?;
const val = known.get(wire) orelse continue;
_ = words.next().?; // drop '->'
const target = words.next().?;
try known.put(target, ~val);
} else if (std.mem.containsAtLeast(u8, line, 1, "AND") or
std.mem.containsAtLeast(u8, line, 1, "OR") or
std.mem.containsAtLeast(u8, line, 1, "LSHIFT") or
std.mem.containsAtLeast(u8, line, 1, "RSHIFT"))
{
const left = words.next().?;
const operand = words.next().?;
const right = words.next().?;
_ = words.next(); // drop '->'
const target = words.next().?;
try handleOp(known, left, operand, right, target);
} else {
const wire = words.next().?;
const val = std.fmt.parseUnsigned(usize, wire, 10) catch known.get(wire) orelse continue;
_ = words.next(); // drop '->'
const target = words.next().?;
// Do not overwrite 'b' value - Part 2
if (std.mem.eql(u8, target, "b") and known.contains("b")) continue;
try known.put(target, val);
}
}
if (known.get("a")) |val| return val;
if (size == known.count()) unreachable;
}
unreachable;
}
fn handleOp(known: *std.StringHashMap(usize), o1: Str, op: Str, o2: Str, target: Str) !void {
const left = std.fmt.parseUnsigned(usize, o1, 10) catch known.get(o1) orelse return;
const right = std.fmt.parseUnsigned(usize, o2, 10) catch known.get(o2) orelse return;
if (std.mem.eql(u8, op, "AND")) {
try known.put(target, left & right);
} else if (std.mem.eql(u8, op, "OR")) {
try known.put(target, left | right);
} else if (std.mem.eql(u8, op, "RSHIFT")) {
try known.put(target, left >> @intCast(u6, right));
} else if (std.mem.eql(u8, op, "LSHIFT")) {
try known.put(target, left << @intCast(u6, right));
} else {
unreachable;
}
}
test "day07a" {
try std.testing.expectEqual(@as(usize, 956), try first(std.testing.allocator));
}
test "day07b" {
try std.testing.expectEqual(@as(usize, 40149), try second(std.testing.allocator));
}