301 lines
7.4 KiB
Zig
301 lines
7.4 KiB
Zig
const std = @import("std");
|
|
const aoc = @import("aoc.zig");
|
|
|
|
const Allocator = std.mem.Allocator;
|
|
const List = std.ArrayList;
|
|
const Map = std.AutoHashMap;
|
|
const Set = aoc.AutoHashSet;
|
|
const PriorityQueue = std.PriorityQueue;
|
|
|
|
pub fn main() !void {
|
|
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const text = try aoc.readAll(allocator, "input/day17.txt");
|
|
defer allocator.free(text);
|
|
|
|
var device = try Device.parse(allocator, text);
|
|
defer device.deinit();
|
|
|
|
var output = try device.run();
|
|
defer output.deinit();
|
|
|
|
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
|
const stdout = bw.writer();
|
|
|
|
const out = try joinOutput(allocator, output.items);
|
|
defer out.deinit();
|
|
|
|
try stdout.print("{s}\n", .{out.items});
|
|
try stdout.print("{d}\n", .{try quine(allocator, device.program)});
|
|
try bw.flush();
|
|
}
|
|
|
|
fn joinOutput(allocator: Allocator, nums: []u3) !List(u8) {
|
|
var buf = List(u8).init(allocator);
|
|
var w = buf.writer();
|
|
|
|
if (nums.len > 0) {
|
|
try w.print("{d}", .{nums[0]});
|
|
}
|
|
|
|
if (nums.len > 1) {
|
|
for (nums[1..]) |n| {
|
|
try w.print(",{d}", .{n});
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
const Device = struct {
|
|
a: u64,
|
|
b: u64,
|
|
c: u64,
|
|
|
|
program: List(u3),
|
|
ip: usize,
|
|
|
|
allocator: Allocator,
|
|
|
|
const Self = @This();
|
|
|
|
fn parse(allocator: Allocator, text: []const u8) !Self {
|
|
var blocks = std.mem.splitSequence(u8, text, "\n\n");
|
|
const a, const b, const c = v: {
|
|
const block = blocks.next().?;
|
|
var lines = std.mem.tokenizeScalar(u8, block, '\n');
|
|
|
|
var line = lines.next().?;
|
|
line = takeLiteral(line, "Register A: ").?;
|
|
line, const a = takeInt(line).?;
|
|
if (line.len != 0) unreachable;
|
|
|
|
line = lines.next().?;
|
|
line = takeLiteral(line, "Register B: ").?;
|
|
line, const b = takeInt(line).?;
|
|
if (line.len != 0) unreachable;
|
|
|
|
line = lines.next().?;
|
|
line = takeLiteral(line, "Register C: ").?;
|
|
line, const c = takeInt(line).?;
|
|
if (line.len != 0) unreachable;
|
|
|
|
break :v .{ a, b, c };
|
|
};
|
|
|
|
var program = List(u3).init(allocator);
|
|
errdefer program.deinit();
|
|
{
|
|
var line = blocks.next().?;
|
|
|
|
line = std.mem.trim(u8, line, " \n");
|
|
line = takeLiteral(line, "Program: ").?;
|
|
|
|
var nums = std.mem.tokenizeScalar(u8, line, ',');
|
|
while (nums.next()) |num| {
|
|
const n = try std.fmt.parseInt(u3, num, 10);
|
|
try program.append(n);
|
|
}
|
|
}
|
|
|
|
return Self{
|
|
.a = a,
|
|
.b = b,
|
|
.c = c,
|
|
|
|
.ip = 0,
|
|
.program = program,
|
|
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
fn deinit(self: *Self) void {
|
|
self.program.deinit();
|
|
}
|
|
|
|
fn run(self: *Self) !List(u3) {
|
|
var output = List(u3).init(self.allocator);
|
|
while (self.ip < self.program.items.len) {
|
|
// self.dbg();
|
|
self.ip, const out = self.tick();
|
|
|
|
if (out) |n| {
|
|
try output.append(@intCast(n));
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
fn dbg(self: Self) void {
|
|
std.debug.print("ip: {}\nA: {}\nB: {}\nC: {}\n", .{ self.ip, self.a, self.b, self.c });
|
|
std.debug.print("op: {} {}\n", .{
|
|
self.program.items[self.ip],
|
|
self.program.items[self.ip + 1],
|
|
});
|
|
const stdin = std.io.getStdIn();
|
|
var buf: [1]u8 = undefined;
|
|
_ = stdin.reader().readUntilDelimiterOrEof(&buf, '\n') catch return orelse return;
|
|
}
|
|
|
|
fn tick(self: *Self) struct { usize, ?u64 } {
|
|
const opcode: Opcode = @enumFromInt(self.program.items[self.ip]);
|
|
const operand = self.program.items[self.ip + 1];
|
|
|
|
const pow = std.math.pow;
|
|
|
|
switch (opcode) {
|
|
.adv => {
|
|
const num = self.a;
|
|
const denom = pow(u64, 2, self.combo(operand));
|
|
self.a = @divTrunc(num, denom);
|
|
|
|
return .{ self.ip + 2, null };
|
|
},
|
|
.bxl => {
|
|
self.b = self.b ^ operand;
|
|
|
|
return .{ self.ip + 2, null };
|
|
},
|
|
.bst => {
|
|
const n = self.combo(operand);
|
|
self.b = n % 8;
|
|
|
|
return .{ self.ip + 2, null };
|
|
},
|
|
.jnz => {
|
|
if (self.a == 0) {
|
|
return .{ self.ip + 2, null };
|
|
} else {
|
|
return .{ operand, null };
|
|
}
|
|
},
|
|
.bxc => {
|
|
self.b = self.b ^ self.c;
|
|
|
|
return .{ self.ip + 2, null };
|
|
},
|
|
.out => {
|
|
const n = self.combo(operand);
|
|
const out = n % 8;
|
|
|
|
return .{ self.ip + 2, out };
|
|
},
|
|
.bdv => {
|
|
const num = self.a;
|
|
const denom = pow(u64, 2, self.combo(operand));
|
|
self.b = @divTrunc(num, denom);
|
|
|
|
return .{ self.ip + 2, null };
|
|
},
|
|
.cdv => {
|
|
const num = self.a;
|
|
const denom = pow(u64, 2, self.combo(operand));
|
|
self.c = @divTrunc(num, denom);
|
|
|
|
return .{ self.ip + 2, null };
|
|
},
|
|
}
|
|
}
|
|
|
|
fn combo(self: *Self, operand: u3) u64 {
|
|
return switch (operand) {
|
|
0 => 0,
|
|
1 => 1,
|
|
2 => 2,
|
|
3 => 3,
|
|
4 => self.a,
|
|
5 => self.b,
|
|
6 => self.c,
|
|
7 => unreachable,
|
|
};
|
|
}
|
|
};
|
|
|
|
const Instruction = struct {
|
|
opcode: u3,
|
|
operand: u3,
|
|
};
|
|
|
|
const Opcode = enum(u3) {
|
|
adv = 0,
|
|
bxl = 1,
|
|
bst = 2,
|
|
jnz = 3,
|
|
bxc = 4,
|
|
out = 5,
|
|
bdv = 6,
|
|
cdv = 7,
|
|
};
|
|
|
|
fn takeLiteral(input: []const u8, want: []const u8) ?[]const u8 {
|
|
if (std.mem.startsWith(u8, input, want)) {
|
|
return input[want.len..];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
fn takeInt(input: []const u8) ?struct { []const u8, u64 } {
|
|
var i: usize = 0;
|
|
var n: u64 = 0;
|
|
|
|
while (i < input.len and std.ascii.isDigit(input[i])) : (i += 1) {
|
|
n *= 10;
|
|
n += input[i] - '0';
|
|
}
|
|
|
|
if (i == 0) {
|
|
// Nothing consumed
|
|
return null;
|
|
}
|
|
|
|
return .{ input[i..], n };
|
|
}
|
|
|
|
fn quine(allocator: Allocator, program: List(u3)) !u64 {
|
|
var stack = List(u64).init(allocator);
|
|
defer stack.deinit();
|
|
|
|
// Easier than figuring out reverse iteration lol
|
|
var possible = Set(u64).init(allocator);
|
|
defer possible.deinit();
|
|
|
|
inline for (0..8) |a| {
|
|
try stack.append(a);
|
|
}
|
|
|
|
while (stack.popOrNull()) |a| {
|
|
var device = Device{
|
|
.a = a,
|
|
.b = 0,
|
|
.c = 0,
|
|
.program = program,
|
|
.ip = 0,
|
|
.allocator = allocator,
|
|
};
|
|
var out = try device.run();
|
|
defer out.deinit();
|
|
|
|
if (std.mem.endsWith(u3, program.items, out.items)) {
|
|
if (program.items.len == out.items.len) {
|
|
try possible.put(a);
|
|
continue;
|
|
}
|
|
|
|
inline for (0..8) |next| {
|
|
try stack.append((a << 3) + next);
|
|
}
|
|
}
|
|
}
|
|
|
|
var min: u64 = possible.pop().?;
|
|
while (possible.pop()) |a| {
|
|
if (a < min) {
|
|
min = a;
|
|
}
|
|
}
|
|
|
|
return min;
|
|
}
|