diff --git a/2024/src/day17.zig b/2024/src/day17.zig new file mode 100644 index 0000000..e01f58c --- /dev/null +++ b/2024/src/day17.zig @@ -0,0 +1,254 @@ +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 bw.flush(); +} + +fn joinOutput(allocator: Allocator, nums: []u64) !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(u64) { + var output = List(u64).init(self.allocator); + while (self.ip < self.program.items.len) { + // self.dbg(); + self.ip, const out = self.tick(); + + if (out) |n| { + try output.append(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 }; +}