From eae81ce631ce4c0fbde8444873d85a7be692742e Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 28 Apr 2024 22:26:18 +0300 Subject: [PATCH] solve day 20 part 1 --- src/day20.zig | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 2 + 2 files changed, 338 insertions(+) create mode 100644 src/day20.zig diff --git a/src/day20.zig b/src/day20.zig new file mode 100644 index 0000000..b14d445 --- /dev/null +++ b/src/day20.zig @@ -0,0 +1,336 @@ +const std = @import("std"); +const aoc = @import("./aoc.zig"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const NameString = std.BoundedArray(u8, 12); + +const ModuleType = enum { + None, + FlipFlop, + Conjunction, +}; + +const Module = struct { + name: NameString, + module_type: ModuleType, + outputs: std.ArrayList(NameString), + + fn deinit(self: Module) void { + self.outputs.deinit(); + } + + fn print(self: Module) void { + if (self.module_type == .FlipFlop) { + std.debug.print("%", .{}); + } else if (self.module_type == .Conjunction) { + std.debug.print("&", .{}); + } + std.debug.print("{s} -> ", .{self.name.slice()}); + for (0.., self.outputs.items) |i, output| { + if (i > 0) { + std.debug.print(", ", .{}); + } + std.debug.print("{s}", .{output.slice()}); + } + std.debug.print("\n", .{}); + } +}; + +const Input = struct { + modules: std.ArrayList(Module), + + fn init(allocator: Allocator) Input { + return Input{ + .modules = std.ArrayList(Module).init(allocator) + }; + } + + fn deinit(self: Input) void { + for (self.modules.items) |module| { + module.deinit(); + } + self.modules.deinit(); + } +}; + +const ModuleId = u8; + +const InternalModule = struct { + const max_inputs = 12; + + module_type: ModuleType, + state: std.bit_set.IntegerBitSet(max_inputs), + inputs: []ModuleId, + outputs: []ModuleId, + + fn flip_flop(self: *@This()) bool { + self.state.mask = ~self.state.mask; + return self.state.mask != 0; + } + + fn update_input(self: *@This(), module_index: ModuleId, is_high: bool) bool { + const index = std.mem.indexOfScalar(ModuleId, self.inputs, module_index); + assert(index != null); + + self.state.setValue(index.?, is_high); + return self.state.count() != self.inputs.len; + } +}; + +fn append_if_new(names: *std.ArrayList(NameString), name: NameString) !void { + for (names.items) |other_name| { + if (std.mem.eql(u8, other_name.slice(), name.slice())) { + return; + } + } + + try names.append(name); +} + +const MachineState = struct { + allocator: Allocator, + names: []NameString, + state: []InternalModule, + + fn init(allocator: Allocator, modules: []Module) !MachineState { + assert(modules.len < (1 << @bitSizeOf(ModuleId))); + + var names = std.ArrayList(NameString).init(allocator); + defer names.deinit(); + for (modules) |module| { + try append_if_new(&names, module.name); + for (module.outputs.items) |output| { + try append_if_new(&names, output); + } + } + + var self = MachineState { + .state = try allocator.alloc(InternalModule, names.items.len), + .names = try allocator.dupe(NameString, names.items), + .allocator = allocator + }; + errdefer self.deinit(); + + @memset(self.state, InternalModule{ + .module_type = .None, + .state = std.bit_set.IntegerBitSet(12).initEmpty(), + .inputs = &[0]ModuleId{}, + .outputs = &[0]ModuleId{}, + }); + + for (modules) |module| { + const module_idx = self.get_name_idx(module.name).?; + var internal_module = &self.state[module_idx]; + + internal_module.module_type = module.module_type; + internal_module.outputs = try allocator.alloc(ModuleId, module.outputs.items.len); + for (0.., module.outputs.items) |output_idx, output_name| { + const index = self.get_name_idx(output_name); + assert(index != null); + internal_module.outputs[output_idx] = @intCast(index.?); + } + } + + for (0.., self.state) |i, *module| { + var input_count: usize = 0; + for (self.state) |other_module| { + if (std.mem.indexOfScalar(ModuleId, other_module.outputs, @intCast(i)) != null) { + input_count += 1; + } + } + + if (input_count == 0) continue; + + var index: usize = 0; + assert(input_count <= InternalModule.max_inputs); + module.inputs = try allocator.alloc(ModuleId, input_count); + for (0.., self.state) |other_i, other_module| { + if (std.mem.indexOfScalar(ModuleId, other_module.outputs, @intCast(i)) != null) { + module.inputs[index] = @intCast(other_i); + index += 1; + } + } + } + + return self; + } + + fn deinit(self: MachineState) void { + for (self.state) |module| { + if (module.inputs.len > 0) { + self.allocator.free(module.inputs); + } + if (module.outputs.len > 0) { + self.allocator.free(module.outputs); + } + } + self.allocator.free(self.state); + self.allocator.free(self.names); + } + + fn get_name_idx(self: MachineState, name: NameString) ?ModuleId { + for (0.., self.names) |i, other_name| { + if (std.mem.eql(u8, other_name.slice(), name.slice())) { + return @intCast(i); + } + } + return null; + } +}; + +fn parse_module(allocator: Allocator, line: []const u8) !Module { + var arrow_split_iter = std.mem.splitSequence(u8, line, "->"); + + const name_with_type = arrow_split_iter.next() orelse return error.NoName; + var name = std.mem.trim(u8, name_with_type, " "); + + var module_type = ModuleType.None; + if (name_with_type[0] == '&') { + module_type = .Conjunction; + name = name[1..]; + } else if (name_with_type[0] == '%') { + module_type = .FlipFlop; + name = name[1..]; + } + + var outputs = std.ArrayList(NameString).init(allocator); + errdefer outputs.deinit(); + + var output_split_iter = std.mem.splitScalar(u8, arrow_split_iter.rest(), ','); + while (output_split_iter.next()) |output_str| { + const trimmed_name = std.mem.trim(u8, output_str, " "); + try outputs.append(try NameString.fromSlice(trimmed_name)); + } + + return Module{ + .name = try NameString.fromSlice(name), + .module_type = module_type, + .outputs = outputs + }; +} + +fn parse_input(allocator: Allocator, lines: []const []const u8) !Input { + var parsed = Input.init(allocator); + errdefer parsed.deinit(); + + for (lines) |line| { + try parsed.modules.append(try parse_module(allocator, line)); + } + + return parsed; +} + +const Pulse = struct { + is_high: bool, + from: ModuleId, + to: ModuleId, +}; + +fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineState, pulse: Pulse) !void { + var to_module = &machine_state.state[pulse.to]; + + var output_is_high = false; + if (to_module.module_type == .None) { + output_is_high = pulse.is_high; + } else if (to_module.module_type == .FlipFlop) { + if (pulse.is_high) return; + output_is_high = to_module.flip_flop(); + } else if (to_module.module_type == .Conjunction) { + output_is_high = to_module.update_input(pulse.from, pulse.is_high); + } else { + unreachable; + } + + for (to_module.outputs) |output| { + try pulse_queue.append(Pulse{ + .is_high = output_is_high, + .to = output, + .from = pulse.to + }); + } +} + +fn simulate_button_press(allocator: Allocator, machine_state: *MachineState, broadcaster: ModuleId) !struct { low: u64, high: u64 } { + var pulses = std.ArrayList(Pulse).init(allocator); + defer pulses.deinit(); + + var low_pulse_count: u64 = 0; + var high_pulse_count: u64 = 0; + + try pulses.append(Pulse{ + .is_high = false, + .to = broadcaster, + .from = broadcaster, + }); + + while (pulses.items.len > 0) { + const pulse: Pulse = pulses.orderedRemove(0); + + if (pulse.is_high) { + high_pulse_count += 1; + } else { + low_pulse_count += 1; + } + + try simulate_pulse(&pulses, machine_state, pulse); + } + + return .{ .low = low_pulse_count, .high = high_pulse_count }; +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + const parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + var machine_state = try MachineState.init(allocator, parsed.modules.items); + defer machine_state.deinit(); + + const broadcaster_idx = machine_state.get_name_idx(try NameString.fromSlice("broadcaster")).?; + + var low_count: u64 = 0; + var high_count: u64 = 0; + for (0..1000) |_| { + const high_low_count = try simulate_button_press(allocator, &machine_state, broadcaster_idx); + low_count += high_low_count.low; + high_count += high_low_count.high; + } + + return .{ .uint = low_count * high_count }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + const parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + var machine_state = try MachineState.init(allocator, parsed.modules.items); + defer machine_state.deinit(); + + return .{ .uint = 0 }; +} + +const example_input1 = [_][]const u8{ + "broadcaster -> a, b, c", + "%a -> b", + "%b -> c", + "%c -> inv", + "&inv -> a", +}; + +const example_input2 = [_][]const u8{ + "broadcaster -> a", + "%a -> inv, con", + "&inv -> b", + "%b -> con", + "&con -> output", +}; + +test "part 1 example 1" { + try aoc.expectAnswerUInt(part1, 32000000, &example_input1); +} + +test "part 1 example 2" { + try aoc.expectAnswerUInt(part1, 11687500, &example_input2); +} diff --git a/src/main.zig b/src/main.zig index 1b7d176..78e8bca 100644 --- a/src/main.zig +++ b/src/main.zig @@ -40,6 +40,7 @@ const Days = [_]aoc.Day{ create_day(@import("./day17.zig")), create_day(@import("./day18.zig")), create_day(@import("./day19.zig")), + create_day(@import("./day20.zig")), }; fn kilobytes(count: u32) u32 { @@ -202,4 +203,5 @@ test { _ = @import("./day17.zig"); _ = @import("./day18.zig"); _ = @import("./day19.zig"); + _ = @import("./day20.zig"); }