const std = @import("std"); const builtin = @import("builtin"); fn readInputFile(allocator: std.mem.Allocator, filename: []const u8) anyerror![]u32 { var result = std.ArrayList(u32).init(allocator); const file = try std.fs.cwd().openFile(filename, .{ .read = true }); defer file.close(); const reader = file.reader(); while (true) { var line: []u8 = undefined; if (builtin.os.tag == .windows) { // NOTE: Read another byte on windows due to two-byte eol. line = reader.readUntilDelimiterAlloc(allocator, '\r', 16) catch break; _ = try reader.readByte(); } else { line = reader.readUntilDelimiterAlloc(allocator, '\n', 16) catch break; } defer allocator.free(line); var message: u32 = 0; for (line) |char| { message |= @as(u32, if (char == '1') 0x1 else 0x0); message <<= 1; } message >>= 1; try result.append(message); } return result.items; } pub fn task1(comptime T: type, diagnostic: []const u32) u32 { var gamma_rate: u32 = 0; var epsilon_rate: u32 = 0; var bit: u5 = 1; while (bit <= @bitSizeOf(T)) : (bit += 1) { var on: u32 = 0; var off: u32 = 0; var message: u32 = 0; while (message < diagnostic.len) : (message += 1) { const bit_val = (diagnostic[message] >> (@bitSizeOf(T) - bit)) & 0x1; if (bit_val == 1) { on += 1; } else { off += 1; } } gamma_rate |= @as(u32, if (on > off) 1 else 0); gamma_rate <<= 1; epsilon_rate |= @as(u32, if (on < off) 1 else 0); epsilon_rate <<= 1; } gamma_rate >>= 1; epsilon_rate >>= 1; std.log.debug("gamma_rate {}", .{gamma_rate}); std.log.debug("epsilon_rate {}", .{epsilon_rate}); return gamma_rate * epsilon_rate; } fn pickDiagnostics(comptime T: type, allocator: std.mem.Allocator, bit: u5, invert: bool, diagnostic: []const u32) []const u32 { var result = std.ArrayList(u32).init(allocator); var on: u32 = 0; var off: u32 = 0; var message: u32 = 0; while (message < diagnostic.len) : (message += 1) { const bit_val = (diagnostic[message] >> (@bitSizeOf(T) - bit)) & 0x1; if (bit_val == 1) { on += 1; } else { off += 1; } } message = 0; while (message < diagnostic.len) : (message += 1) { var bit_val = (diagnostic[message] >> (@bitSizeOf(T) - bit)) & 0x1; bit_val = if (invert) (~bit_val) & 0x1 else bit_val; if ((on >= off and bit_val == 1) or (on < off and bit_val == 0)) { result.append(diagnostic[message]) catch unreachable; } } return result.items; } pub fn task2(comptime T: type, diagnostic: []const u32) u32 { var buffer: [65536]u8 = undefined; const allocator = std.heap.FixedBufferAllocator.init(&buffer).allocator(); var oxygen_rating: u32 = 0; var co2_rating: u32 = 0; var filtered_oxygen: []const u32 = allocator.dupe(u32, diagnostic) catch unreachable; var filtered_co2: []const u32 = allocator.dupe(u32, diagnostic) catch unreachable; var bit: u5 = 1; while (bit <= @bitSizeOf(T)) : (bit += 1) { if (filtered_oxygen.len > 1) { var prev = filtered_oxygen; filtered_oxygen = pickDiagnostics(T, allocator, bit, false, prev); allocator.free(prev); } if (filtered_co2.len > 1) { var prev = filtered_co2; filtered_co2 = pickDiagnostics(T, allocator, bit, true, prev); allocator.free(prev); } } oxygen_rating = filtered_oxygen[0]; co2_rating = filtered_co2[0]; std.log.debug("oxygen_rating {}", .{oxygen_rating}); std.log.debug("co2_rating {}", .{co2_rating}); return oxygen_rating * co2_rating; } pub fn main() anyerror!void { var buffer: [65536]u8 = undefined; var fixed_buffer = std.heap.FixedBufferAllocator.init(&buffer); const allocator = fixed_buffer.allocator(); const input = try readInputFile(allocator, "input.txt"); const task_1_result = task1(u12, input); std.log.info("Task 1 result: {}", .{task_1_result}); const task_2_result = task2(u12, input); std.log.info("Task 2 result: {}", .{task_2_result}); }