156 lines
5.3 KiB
Zig
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});
|
|
}
|