156 lines
5.3 KiB
Zig

const std = @import("std");
const utils = @import("./utils.zig");
const InstructionType = enum { LITERAL, REASSIGN, NOT, AND, HACKY_AND, OR, LSHIFT, RSHIFT };
const Instruction = struct {
type: InstructionType,
// To be used with LITERAL
num: ?u16,
firstLetter: ?[]const u8,
secondLetter: ?[]const u8,
};
fn getOp(op: []const u8) InstructionType {
if (std.mem.eql(u8, op, "AND")) {
return InstructionType.AND;
}
if (std.mem.eql(u8, op, "OR")) {
return InstructionType.OR;
}
if (std.mem.eql(u8, op, "LSHIFT")) {
return InstructionType.LSHIFT;
}
if (std.mem.eql(u8, op, "RSHIFT")) {
return InstructionType.RSHIFT;
}
unreachable;
}
fn getLetter(map: std.StringHashMap(Instruction), cache: *std.StringHashMap(u16), letter: []const u8) !u16 {
if (cache.contains(letter)) {
return cache.*.get(letter).?;
}
var op = map.get(letter).?;
if (op.type == InstructionType.LITERAL) {
try cache.*.put(letter, op.num.?);
return op.num.?;
}
if (op.type == InstructionType.NOT) {
var notRes = ~(try getLetter(map, cache, op.firstLetter.?));
try cache.*.put(letter, notRes);
return notRes;
}
if (op.type == InstructionType.REASSIGN) {
var reassignRes = try getLetter(map, cache, op.firstLetter.?);
try cache.*.put(letter, reassignRes);
return reassignRes;
}
var first = try getLetter(map, cache, op.firstLetter.?);
if (op.type == InstructionType.HACKY_AND) {
try cache.*.put(letter, op.num.? & first);
return op.num.? & first;
}
if (op.type == InstructionType.AND or op.type == InstructionType.OR) {
var second = try getLetter(map, cache, op.secondLetter.?);
if (op.type == InstructionType.AND) {
try cache.*.put(letter, first & second);
return first & second;
}
try cache.*.put(letter, first | second);
return first | second;
}
if (op.type == InstructionType.LSHIFT) {
var shortNum: u4 = @truncate(op.num.?);
try cache.*.put(letter, first << shortNum);
return first << shortNum;
}
var shortNum: u4 = @truncate(op.num.?);
try cache.*.put(letter, first >> shortNum);
return first >> shortNum;
}
pub fn main() !void {
var allocator = std.heap.page_allocator;
var lines = try utils.splitLines("input.txt", allocator);
defer lines.deinit();
var wireMap = std.StringHashMap(Instruction).init(allocator);
defer wireMap.deinit();
var resultCache = std.StringHashMap(u16).init(allocator);
defer resultCache.deinit();
for (lines.items) |line| {
var instructionAndOutput = std.mem.tokenizeSequence(u8, line, " -> ");
var op = instructionAndOutput.next().?;
var output = instructionAndOutput.next().?;
var instructionParts = std.mem.tokenizeSequence(u8, op, " ");
var first = instructionParts.next().?;
if (std.mem.eql(u8, first, "NOT")) {
var notOp = Instruction{ .type = InstructionType.NOT, .firstLetter = instructionParts.next().?, .num = undefined, .secondLetter = undefined };
try wireMap.put(output, notOp);
continue;
}
var firstNumber = std.fmt.parseInt(u16, first, 10) catch {
// Must be a regular double operation (a AND b)
if (instructionParts.next()) |rawOperator| {
var operator = getOp(rawOperator);
var second = instructionParts.next().?;
var myOp = switch (operator) {
InstructionType.AND, InstructionType.OR => Instruction{ .type = operator, .firstLetter = first, .secondLetter = second, .num = undefined },
InstructionType.LSHIFT, InstructionType.RSHIFT => Instruction{ .type = operator, .firstLetter = first, .secondLetter = undefined, .num = try std.fmt.parseInt(u4, second, 10) },
else => unreachable,
};
try wireMap.put(output, myOp);
continue;
}
// x -> y
var reassign = Instruction{ .type = InstructionType.REASSIGN, .firstLetter = first, .secondLetter = undefined, .num = undefined };
try wireMap.put(output, reassign);
continue;
};
// Edge case. and gates can have numbers too (seems to be limited to first argument).
if (instructionParts.next()) |_| {
// an and operation with a 1 in front.
var second = instructionParts.next().?;
var hackyAnd = Instruction{ .type = InstructionType.HACKY_AND, .firstLetter = second, .secondLetter = undefined, .num = firstNumber };
try wireMap.put(output, hackyAnd);
continue;
}
var literalOp = Instruction{ .type = InstructionType.LITERAL, .num = firstNumber, .firstLetter = undefined, .secondLetter = undefined };
try wireMap.put(output, literalOp);
}
var part1 = try getLetter(wireMap, &resultCache, "a");
std.debug.print("Part 1: {}\n", .{part1});
var resetResultCache = std.StringHashMap(u16).init(allocator);
defer resetResultCache.deinit();
try resetResultCache.put("b", part1);
var part2 = try getLetter(wireMap, &resetResultCache, "a");
std.debug.print("Part 2: {}\n", .{part2});
}