2024 Day 24 Part 2
This commit is contained in:
parent
08c71c07d4
commit
d5db840354
1 changed files with 253 additions and 21 deletions
|
|
@ -16,6 +16,11 @@ pub fn main() !void {
|
||||||
const text = try aoc.readAll(allocator, "input/day24.txt");
|
const text = try aoc.readAll(allocator, "input/day24.txt");
|
||||||
defer allocator.free(text);
|
defer allocator.free(text);
|
||||||
|
|
||||||
|
const text2 = try aoc.readAll(allocator, "input/day24_swaps.txt");
|
||||||
|
defer allocator.free(text2);
|
||||||
|
const swaps = try aoc.parseAll(Swap, allocator, text2, "\n");
|
||||||
|
defer swaps.deinit();
|
||||||
|
|
||||||
var blocks = std.mem.tokenizeSequence(u8, text, "\n\n");
|
var blocks = std.mem.tokenizeSequence(u8, text, "\n\n");
|
||||||
|
|
||||||
var inputs = try aoc.parseAll(Input, allocator, blocks.next().?, "\n");
|
var inputs = try aoc.parseAll(Input, allocator, blocks.next().?, "\n");
|
||||||
|
|
@ -29,6 +34,12 @@ pub fn main() !void {
|
||||||
|
|
||||||
try stdout.print("{}\n", .{try part1(allocator, inputs.items, gates.items)});
|
try stdout.print("{}\n", .{try part1(allocator, inputs.items, gates.items)});
|
||||||
try bw.flush();
|
try bw.flush();
|
||||||
|
|
||||||
|
var wires = try part2(allocator, gates.items, swaps.items);
|
||||||
|
defer wires.deinit();
|
||||||
|
|
||||||
|
try stdout.print("{s}\n", .{wires.items});
|
||||||
|
try bw.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
const String = []const u8;
|
const String = []const u8;
|
||||||
|
|
@ -49,6 +60,21 @@ const Input = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Swap = struct {
|
||||||
|
a: String,
|
||||||
|
b: String,
|
||||||
|
|
||||||
|
pub fn parse(_: Allocator, line: String) !Swap {
|
||||||
|
var fields = std.mem.tokenizeSequence(u8, line, " ");
|
||||||
|
const a = fields.next().?;
|
||||||
|
const b = fields.next().?;
|
||||||
|
return Swap{
|
||||||
|
.a = a,
|
||||||
|
.b = b,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Gate = struct {
|
const Gate = struct {
|
||||||
a: String,
|
a: String,
|
||||||
b: String,
|
b: String,
|
||||||
|
|
@ -57,12 +83,18 @@ const Gate = struct {
|
||||||
|
|
||||||
pub fn parse(_: Allocator, line: String) !Gate {
|
pub fn parse(_: Allocator, line: String) !Gate {
|
||||||
var fields = std.mem.tokenizeSequence(u8, line, " ");
|
var fields = std.mem.tokenizeSequence(u8, line, " ");
|
||||||
const a = fields.next().?;
|
var a = fields.next().?;
|
||||||
const op = fields.next().?;
|
const op = fields.next().?;
|
||||||
const b = fields.next().?;
|
var b = fields.next().?;
|
||||||
if (!std.mem.eql(u8, fields.next().?, "->")) unreachable;
|
if (!std.mem.eql(u8, fields.next().?, "->")) unreachable;
|
||||||
const out = fields.next().?;
|
const out = fields.next().?;
|
||||||
|
|
||||||
|
if (std.mem.lessThan(u8, b, a)) {
|
||||||
|
const t = a;
|
||||||
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
return Gate{
|
return Gate{
|
||||||
.a = a,
|
.a = a,
|
||||||
.b = b,
|
.b = b,
|
||||||
|
|
@ -70,6 +102,31 @@ const Gate = struct {
|
||||||
.op = Op.parse(op).?,
|
.op = Op.parse(op).?,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(
|
||||||
|
self: Gate,
|
||||||
|
comptime _: []const u8,
|
||||||
|
_: std.fmt.FormatOptions,
|
||||||
|
writer: anytype,
|
||||||
|
) !void {
|
||||||
|
try writer.print("{s} {s} {s} -> {s}", .{ self.a, self.op, self.b, self.out });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hasInputs(self: Gate, a: String, b: String) bool {
|
||||||
|
return (streq(self.a, a) and streq(self.b, b)) or (streq(self.a, b) and streq(self.b, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hasOutput(self: Gate, out: String) bool {
|
||||||
|
return streq(self.out, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mustOutput(self: Gate, out: String) Gate {
|
||||||
|
if (self.hasOutput(out)) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
std.debug.print("want: {s}, got: {s}\n", .{ out, self.out });
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Op = enum {
|
const Op = enum {
|
||||||
|
|
@ -92,34 +149,26 @@ const Op = enum {
|
||||||
.Xor => a ^ b,
|
.Xor => a ^ b,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(
|
||||||
|
self: Op,
|
||||||
|
comptime _: []const u8,
|
||||||
|
_: std.fmt.FormatOptions,
|
||||||
|
writer: anytype,
|
||||||
|
) !void {
|
||||||
|
const s = switch (self) {
|
||||||
|
.And => "AND",
|
||||||
|
.Or => "_OR",
|
||||||
|
.Xor => "XOR",
|
||||||
|
};
|
||||||
|
try writer.print("{s}", .{s});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn part1(allocator: Allocator, inputs: []const Input, gates: []const Gate) !u64 {
|
fn part1(allocator: Allocator, inputs: []const Input, gates: []const Gate) !u64 {
|
||||||
var values = StringMap(u1).init(allocator);
|
var values = try eval(allocator, gates, inputs);
|
||||||
defer values.deinit();
|
defer values.deinit();
|
||||||
|
|
||||||
for (inputs) |i| {
|
|
||||||
try values.put(i.name, i.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var pending = List(Gate).init(allocator);
|
|
||||||
try pending.appendSlice(gates);
|
|
||||||
|
|
||||||
while (pending.items.len > 0) {
|
|
||||||
var next = List(Gate).init(allocator);
|
|
||||||
for (pending.items) |g| {
|
|
||||||
if (values.contains(g.a) and values.contains(g.b)) {
|
|
||||||
const a = values.get(g.a).?;
|
|
||||||
const b = values.get(g.b).?;
|
|
||||||
try values.put(g.out, g.op.eval(a, b));
|
|
||||||
} else {
|
|
||||||
try next.append(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pending.deinit();
|
|
||||||
pending = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputs = List(String).init(allocator);
|
var outputs = List(String).init(allocator);
|
||||||
defer outputs.deinit();
|
defer outputs.deinit();
|
||||||
{
|
{
|
||||||
|
|
@ -132,6 +181,7 @@ fn part1(allocator: Allocator, inputs: []const Input, gates: []const Gate) !u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std.mem.sort(String, outputs.items, {}, gtString);
|
std.mem.sort(String, outputs.items, {}, gtString);
|
||||||
|
if (outputs.items.len != 46) unreachable;
|
||||||
|
|
||||||
var n: u64 = 0;
|
var n: u64 = 0;
|
||||||
for (outputs.items) |z| {
|
for (outputs.items) |z| {
|
||||||
|
|
@ -143,6 +193,188 @@ fn part1(allocator: Allocator, inputs: []const Input, gates: []const Gate) !u64
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval(allocator: Allocator, gates: []const Gate, inputs: []const Input) !StringMap(u1) {
|
||||||
|
var values = StringMap(u1).init(allocator);
|
||||||
|
for (inputs) |i| {
|
||||||
|
try values.put(i.name, i.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pending = List(Gate).init(allocator);
|
||||||
|
try pending.appendSlice(gates);
|
||||||
|
|
||||||
|
while (pending.items.len > 0) {
|
||||||
|
var next = List(Gate).init(allocator);
|
||||||
|
var changed = false;
|
||||||
|
for (pending.items) |g| {
|
||||||
|
if (values.contains(g.a) and values.contains(g.b)) {
|
||||||
|
const a = values.get(g.a).?;
|
||||||
|
const b = values.get(g.b).?;
|
||||||
|
try values.put(g.out, g.op.eval(a, b));
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
try next.append(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
// No progress, drop everything and quit.
|
||||||
|
pending.deinit();
|
||||||
|
next.deinit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending.deinit();
|
||||||
|
pending = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ltString(_: void, a: String, b: String) bool {
|
||||||
|
return std.mem.lessThan(u8, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
fn gtString(_: void, a: String, b: String) bool {
|
fn gtString(_: void, a: String, b: String) bool {
|
||||||
return std.mem.lessThan(u8, b, a);
|
return std.mem.lessThan(u8, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn streq(a: String, b: String) bool {
|
||||||
|
return std.mem.eql(u8, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(allocator: Allocator, circuit: []const Gate, swaps: []const Swap) !List(u8) {
|
||||||
|
if (swaps.len == 4) {
|
||||||
|
return try formatWires(allocator, swaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
var renames = StringMap(String).init(allocator);
|
||||||
|
defer renames.deinit();
|
||||||
|
for (swaps) |swap| {
|
||||||
|
try renames.put(swap.a, swap.b);
|
||||||
|
try renames.put(swap.b, swap.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixed = List(Gate).init(allocator);
|
||||||
|
defer fixed.deinit();
|
||||||
|
for (circuit) |g| {
|
||||||
|
try fixed.append(Gate{
|
||||||
|
.out = renames.get(g.out) orelse g.out,
|
||||||
|
.a = g.a,
|
||||||
|
.b = g.b,
|
||||||
|
.op = g.op,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const gates = fixed.items;
|
||||||
|
|
||||||
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const str = arena.allocator();
|
||||||
|
|
||||||
|
// Assumption: Each input wire is used in exactly one one-bit adder.
|
||||||
|
// Assumption: The input wires are never OR-ed directly for carries.
|
||||||
|
for (0..45) |i| {
|
||||||
|
const x = try std.fmt.allocPrint(str, "x{d:0>2}", .{i});
|
||||||
|
const y = try std.fmt.allocPrint(str, "y{d:0>2}", .{i});
|
||||||
|
|
||||||
|
var xors: usize = 0;
|
||||||
|
var ands: usize = 0;
|
||||||
|
for (gates) |g| {
|
||||||
|
if (!g.hasInputs(x, y)) continue;
|
||||||
|
|
||||||
|
if (g.op == .Xor) xors += 1;
|
||||||
|
if (g.op == .And) ands += 1;
|
||||||
|
if (g.op == .Or) unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xors != 1) unreachable;
|
||||||
|
if (ands != 1) unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lambda = struct {
|
||||||
|
fn findGate(gs: []const Gate, i: String, j: String, op: Op) ?Gate {
|
||||||
|
return for (gs) |g| {
|
||||||
|
if (g.hasInputs(i, j) and g.op == op) break g;
|
||||||
|
} else null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn findAdder(gs: []const Gate, i: String, j: String) ?struct { Gate, Gate } {
|
||||||
|
const lo = for (gs) |g| {
|
||||||
|
if (g.hasInputs(i, j) and g.op == .Xor) {
|
||||||
|
break g;
|
||||||
|
}
|
||||||
|
} else return null;
|
||||||
|
|
||||||
|
const hi = for (gs) |g| {
|
||||||
|
if (g.hasInputs(i, j) and g.op == .And) {
|
||||||
|
break g;
|
||||||
|
}
|
||||||
|
} else return null;
|
||||||
|
return .{ lo, hi };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 00
|
||||||
|
const a00, const b00 = lambda.findAdder(gates, "x00", "y00") orelse unreachable;
|
||||||
|
std.debug.print("> a00 : {s}\n", .{a00});
|
||||||
|
std.debug.print("> b00 : {s}\n", .{b00});
|
||||||
|
|
||||||
|
const z00 = a00.mustOutput("z00");
|
||||||
|
const c00 = b00;
|
||||||
|
std.debug.print("> c00 : {s}\n", .{c00});
|
||||||
|
std.debug.print("> z00 : {s}\n", .{z00});
|
||||||
|
|
||||||
|
// 01 - end
|
||||||
|
var c = List(String).init(allocator);
|
||||||
|
defer c.deinit();
|
||||||
|
try c.append(c00.out);
|
||||||
|
|
||||||
|
for (1..45) |i| {
|
||||||
|
const x = try std.fmt.allocPrint(str, "x{d:0>2}", .{i});
|
||||||
|
const y = try std.fmt.allocPrint(str, "y{d:0>2}", .{i});
|
||||||
|
const z = try std.fmt.allocPrint(str, "z{d:0>2}", .{i});
|
||||||
|
|
||||||
|
const a, const b = lambda.findAdder(gates, x, y) orelse unreachable;
|
||||||
|
std.debug.print("> a{d:0>2} : {s}\n", .{ i, a });
|
||||||
|
std.debug.print("> b{d:0>2} : {s}\n", .{ i, b });
|
||||||
|
|
||||||
|
const d, const e = lambda.findAdder(gates, c.items[i - 1], a.out) orelse unreachable;
|
||||||
|
std.debug.print("> d{d:0>2} : {s}\n", .{ i, d });
|
||||||
|
std.debug.print("> e{d:0>2} : {s}\n", .{ i, e });
|
||||||
|
|
||||||
|
const zz = d.mustOutput(z);
|
||||||
|
std.debug.print("> z{d:0>2} : {s}\n", .{ i, zz });
|
||||||
|
|
||||||
|
const cc = lambda.findGate(gates, e.out, b.out, .Or) orelse unreachable;
|
||||||
|
std.debug.print("> c{d:0>2} : {s}\n", .{ i, cc });
|
||||||
|
|
||||||
|
try c.append(cc.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn formatWires(allocator: Allocator, swaps: []const Swap) !List(u8) {
|
||||||
|
var names = List(String).init(allocator);
|
||||||
|
defer names.deinit();
|
||||||
|
for (swaps) |swap| {
|
||||||
|
try names.append(swap.a);
|
||||||
|
try names.append(swap.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
std.mem.sort(String, names.items, {}, ltString);
|
||||||
|
|
||||||
|
var buf = List(u8).init(allocator);
|
||||||
|
var w = buf.writer();
|
||||||
|
|
||||||
|
if (names.items.len > 0) {
|
||||||
|
try w.print("{s}", .{names.items[0]});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (names.items.len > 1) {
|
||||||
|
for (names.items[1..]) |n| {
|
||||||
|
try w.print(",{s}", .{n});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue