a little bit late, but Family and stuff. Also I challenged myself to solve this puzzle completely in comptime. During runtime only the result should be printed. I finally did it (for part 1):
Solution 1 (comptime only)
const std = @import("std");
//const input = @embedFile("./small.inp");
//const input = @embedFile("./small2.inp");
//const input = @embedFile("./medium.inp");
const input = @embedFile("./big.inp");
const maxBranch = 10_000_000;
// Wires
const emptyStr = " ";
const maxWires = 1000;
fn getWires(comptime inp: []const u8) [maxWires][3:0]u8 {
@setEvalBranchQuota(maxBranch);
var list: [maxWires][3:0]u8 = undefined;
for (&list) |*elem| {
elem[0] = emptyStr[0];
elem[1] = emptyStr[1];
elem[2] = emptyStr[2];
}
var lines = std.mem.tokenizeScalar(u8, inp, '\n');
var idx: usize = 0;
while (lines.next()) |line| {
var tmplist: [3][]const u8 = undefined;
var app = false;
if (line.len > 10 and //
(std.mem.eql(u8, line[4..7], "XOR") or //
std.mem.eql(u8, line[4..7], "AND")))
{
app = true;
tmplist[0] = line[0..3];
tmplist[1] = line[8..11];
tmplist[2] = line[15..18];
} else if (std.mem.eql(u8, line[4..6], "OR")) {
app = true;
tmplist[0] = line[0..3];
tmplist[1] = line[7..10];
tmplist[2] = line[14..17];
}
if (app) {
for (tmplist) |str| {
var inlist = false;
for (list) |elem| {
if (std.mem.eql(u8, &elem, str)) {
inlist = true;
break;
}
}
if (!inlist) {
list[idx][0] = str[0];
list[idx][1] = str[1];
list[idx][2] = str[2];
idx += 1;
}
}
}
}
return list;
}
pub fn createWiresType() type {
@setEvalBranchQuota(maxBranch);
const wires = comptime getWires(input);
comptime var nfields = 0;
for (wires) |wire| {
if (!std.mem.eql(u8, &wire, emptyStr)) {
nfields += 1;
} else {
break;
}
}
var fields: [nfields]std.builtin.Type.StructField = undefined;
for (wires[0..nfields], 0..) |wire, idx| {
fields[idx] = .{ //
.name = &wire,
.type = i8,
.default_value = null,
.is_comptime = false,
.alignment = 0,
};
}
return @Type(.{
.Struct = .{
.layout = .auto,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
.backing_integer = null,
},
});
}
pub fn initWires(comptime inp: []const u8, comptime wires: *Wires_t) void {
@setEvalBranchQuota(maxBranch);
comptime {
for (@typeInfo(Wires_t).Struct.fields) |field| {
@field(wires, field.name) = 0;
}
}
var blocks = std.mem.tokenizeSequence(u8, inp, "\n\n");
var lines = std.mem.tokenizeScalar(u8, blocks.next().?, '\n');
while (lines.next()) |line| {
const initval = if (line[5] == '1') 1 else 0;
@field(wires, line[0..3]) = initval;
}
}
const Wires_t = createWiresType();
pub fn wireOutput(comptime wires: Wires_t) usize {
@setEvalBranchQuota(maxBranch);
var output: usize = 0;
comptime {
for (@typeInfo(Wires_t).Struct.fields) |field| {
if (field.name[0] == 'z') {
const val: usize = @field(wires, field.name);
const idx: u6 = 10 * (field.name[1] - '0') + field.name[2] - '0';
output += val << idx;
}
}
}
return output;
}
pub fn printWires(wires: Wires_t) void {
@setEvalBranchQuota(maxBranch);
inline for (comptime @typeInfo(Wires_t).Struct.fields) |field| {
std.debug.print("{s}: {b}\n", .{ field.name, @field(wires, field.name) });
}
}
// Gates
const maxGates = maxWires;
const ops = enum { and_op, or_op, xor_op };
const Gate_t = struct {
inp1: []const u8,
inp2: []const u8,
out: []const u8,
op: ops,
};
const Gates_t = struct {
ngates: usize,
gates: [maxGates]Gate_t,
};
pub fn initGates(inp: []const u8, gates: *Gates_t) void {
@setEvalBranchQuota(maxBranch);
gates.ngates = 0;
var blocks = std.mem.tokenizeSequence(u8, inp, "\n\n");
_ = blocks.next();
var lines = std.mem.tokenizeScalar(u8, blocks.next().?, '\n');
while (lines.next()) |line| {
var field = std.mem.tokenizeScalar(u8, line, ' ');
const inp1 = field.next().?;
const opstr = field.next().?;
const inp2 = field.next().?;
_ = field.next();
const out = field.next().?;
var op: ops = undefined;
if (std.mem.eql(u8, opstr, "AND")) {
op = .and_op;
} else if (std.mem.eql(u8, opstr, "OR")) {
op = .or_op;
} else if (std.mem.eql(u8, opstr, "XOR")) {
op = .xor_op;
}
gates.gates[gates.ngates] = Gate_t{
.inp1 = inp1,
.inp2 = inp2,
.op = op,
.out = out,
};
gates.ngates += 1;
}
}
pub fn printGates(gates: Gates_t) void {
@setEvalBranchQuota(maxBranch);
for (0..gates.ngates) |i| {
const gate = gates.gates[i];
const opstr = switch (gate.op) {
.and_op => "AND",
.or_op => "OR ",
.xor_op => "XOR",
};
std.debug.print("{s} {s} {s} -> {s}\n", //
.{ gate.inp1, opstr, gate.inp2, gate.out });
}
}
pub fn computeWire(comptime wires: *Wires_t, comptime gates: Gates_t, out: []const u8) void {
@setEvalBranchQuota(maxBranch);
if (out[0] == 'x') {
return;
}
for (0..gates.ngates) |i| {
const gate = gates.gates[i];
if (std.mem.eql(u8, gate.out[0..3], out[0..3])) {
computeWire(wires, gates, gate.inp1);
computeWire(wires, gates, gate.inp2);
switch (gate.op) {
.and_op => { //
@field(wires, gate.out) |= //
@field(wires, gate.inp1) //
& //
@field(wires, gate.inp2);
},
.or_op => {
@field(wires, gate.out) |= //
@field(wires, gate.inp1) //
| //
@field(wires, gate.inp2);
},
.xor_op => {
@field(wires, gate.out) |= //
@field(wires, gate.inp1) //
^ //
@field(wires, gate.inp2);
},
}
}
}
}
pub fn computeCircuit(comptime wires: *Wires_t, comptime gates: Gates_t) void {
@setEvalBranchQuota(maxBranch);
for (0..gates.ngates) |i| {
const gate = gates.gates[i];
if (gate.out[0] == 'z') {
computeWire(wires, gates, gate.out);
}
}
}
pub fn main() !void {
@setEvalBranchQuota(maxBranch);
comptime var wires: Wires_t = undefined;
comptime initWires(input, &wires);
comptime var gates: Gates_t = undefined;
comptime initGates(input, &gates);
//printWires(wires);
//printGates(gates);
//std.debug.print("\n", .{});
//printGates(gates);
comptime computeCircuit(&wires, gates);
//printWires(wires);
const output = comptime wireOutput(wires);
std.debug.print("Output wire signals are: {b} => {d}\n", .{ output, output });
}