solve day 20 part 2
This commit is contained in:
parent
eae81ce631
commit
6feb2bb483
193
src/day20.zig
193
src/day20.zig
@ -59,8 +59,10 @@ const ModuleId = u8;
|
|||||||
const InternalModule = struct {
|
const InternalModule = struct {
|
||||||
const max_inputs = 12;
|
const max_inputs = 12;
|
||||||
|
|
||||||
|
const StateBitSet = std.bit_set.IntegerBitSet(max_inputs);
|
||||||
|
|
||||||
module_type: ModuleType,
|
module_type: ModuleType,
|
||||||
state: std.bit_set.IntegerBitSet(max_inputs),
|
state: StateBitSet,
|
||||||
inputs: []ModuleId,
|
inputs: []ModuleId,
|
||||||
outputs: []ModuleId,
|
outputs: []ModuleId,
|
||||||
|
|
||||||
@ -177,6 +179,16 @@ const MachineState = struct {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(self: MachineState, id: ModuleId) []const u8 {
|
||||||
|
return self.names[id].constSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(self: *MachineState) void {
|
||||||
|
for (self.state) |*module| {
|
||||||
|
module.state = InternalModule.StateBitSet.initEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_module(allocator: Allocator, line: []const u8) !Module {
|
fn parse_module(allocator: Allocator, line: []const u8) !Module {
|
||||||
@ -226,6 +238,10 @@ const Pulse = struct {
|
|||||||
from: ModuleId,
|
from: ModuleId,
|
||||||
to: ModuleId,
|
to: ModuleId,
|
||||||
};
|
};
|
||||||
|
const PulseCount = struct {
|
||||||
|
low: u64,
|
||||||
|
high: u64
|
||||||
|
};
|
||||||
|
|
||||||
fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineState, pulse: Pulse) !void {
|
fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineState, pulse: Pulse) !void {
|
||||||
var to_module = &machine_state.state[pulse.to];
|
var to_module = &machine_state.state[pulse.to];
|
||||||
@ -251,7 +267,7 @@ fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simulate_button_press(allocator: Allocator, machine_state: *MachineState, broadcaster: ModuleId) !struct { low: u64, high: u64 } {
|
fn simulate_button_press(allocator: Allocator, machine_state: *MachineState, broadcaster: ModuleId) !PulseCount {
|
||||||
var pulses = std.ArrayList(Pulse).init(allocator);
|
var pulses = std.ArrayList(Pulse).init(allocator);
|
||||||
defer pulses.deinit();
|
defer pulses.deinit();
|
||||||
|
|
||||||
@ -300,6 +316,165 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
return .{ .uint = low_count * high_count };
|
return .{ .uint = low_count * high_count };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MachineCounter = struct {
|
||||||
|
flip_flops: []ModuleId,
|
||||||
|
input: ModuleId,
|
||||||
|
output: ModuleId
|
||||||
|
};
|
||||||
|
|
||||||
|
const MachineAnalysis = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
counters: []MachineCounter,
|
||||||
|
counter_merge: ModuleId,
|
||||||
|
|
||||||
|
fn deinit(self: MachineAnalysis) void {
|
||||||
|
for (self.counters) |counter| {
|
||||||
|
self.allocator.free(counter.flip_flops);
|
||||||
|
}
|
||||||
|
self.allocator.free(self.counters);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModuleIdHashSet = std.AutoHashMap(ModuleId, void);
|
||||||
|
|
||||||
|
fn find_coverage(allocator: Allocator, machine_state: MachineState, from: ModuleId) !ModuleIdHashSet {
|
||||||
|
var seen = ModuleIdHashSet.init(allocator);
|
||||||
|
errdefer seen.deinit();
|
||||||
|
|
||||||
|
var stack = std.ArrayList(ModuleId).init(allocator);
|
||||||
|
defer stack.deinit();
|
||||||
|
|
||||||
|
try stack.append(from);
|
||||||
|
try seen.put(from, {});
|
||||||
|
|
||||||
|
while (stack.popOrNull()) |node_idx| {
|
||||||
|
for (machine_state.state[node_idx].outputs) |output| {
|
||||||
|
if (seen.contains(output)) continue;
|
||||||
|
|
||||||
|
try stack.append(output);
|
||||||
|
try seen.put(output, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return seen;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_machine(allocator: Allocator, machine_state: MachineState, input_module: ModuleId, output_module: ModuleId) !MachineAnalysis {
|
||||||
|
const counters = try allocator.alloc(MachineCounter, machine_state.state[input_module].outputs.len);
|
||||||
|
errdefer allocator.free(counters);
|
||||||
|
|
||||||
|
var coverage_counts = try allocator.alloc(u32, machine_state.state.len);
|
||||||
|
defer allocator.free(coverage_counts);
|
||||||
|
@memset(coverage_counts, 0);
|
||||||
|
|
||||||
|
for (0.., machine_state.state[input_module].outputs) |i, counter_input| {
|
||||||
|
var coverage = try find_coverage(allocator, machine_state, counter_input);
|
||||||
|
defer coverage.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
var coverage_iter = coverage.keyIterator();
|
||||||
|
while (coverage_iter.next()) |covered_module| {
|
||||||
|
coverage_counts[covered_module.*] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
counters[i].input = counter_input;
|
||||||
|
|
||||||
|
assert(coverage.contains(output_module));
|
||||||
|
counters[i].output = output_module: {
|
||||||
|
var prev = output_module;
|
||||||
|
var current = output_module;
|
||||||
|
outer: while (true) {
|
||||||
|
var covered_input: ?ModuleId = null;
|
||||||
|
for (machine_state.state[current].inputs) |input| {
|
||||||
|
if (!coverage.contains(input)) continue;
|
||||||
|
|
||||||
|
if (covered_input != null) break :outer; // If a second covered input was found, stop search
|
||||||
|
covered_input = input;
|
||||||
|
}
|
||||||
|
if (covered_input == null) break;
|
||||||
|
|
||||||
|
prev = current;
|
||||||
|
current = covered_input.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :output_module prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
var flip_flop_count: u32 = 0;
|
||||||
|
{
|
||||||
|
var iter = coverage.keyIterator();
|
||||||
|
while (iter.next()) |id| {
|
||||||
|
if (machine_state.state[id.*].module_type == .FlipFlop) {
|
||||||
|
flip_flop_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
counters[i].flip_flops = try allocator.alloc(ModuleId, flip_flop_count);
|
||||||
|
{
|
||||||
|
var flip_flop_i: usize = 0;
|
||||||
|
var iter = coverage.keyIterator();
|
||||||
|
while (iter.next()) |id| {
|
||||||
|
if (machine_state.state[id.*].module_type == .FlipFlop) {
|
||||||
|
counters[i].flip_flops[flip_flop_i] = id.*;
|
||||||
|
flip_flop_i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter_merge: ?ModuleId = null;
|
||||||
|
for (0.., coverage_counts) |id, count| {
|
||||||
|
if (count != machine_state.state[input_module].outputs.len) continue;
|
||||||
|
if (id == output_module) continue;
|
||||||
|
|
||||||
|
counter_merge = @intCast(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter_merge == null) return error.NoMergeModule;
|
||||||
|
|
||||||
|
return MachineAnalysis{
|
||||||
|
.allocator = allocator,
|
||||||
|
.counters = counters,
|
||||||
|
.counter_merge = counter_merge.?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_counter_cycle_size(allocator: Allocator, machine_state: *MachineState, counter: MachineCounter, input_module: ModuleId) !u64 {
|
||||||
|
machine_state.reset();
|
||||||
|
|
||||||
|
var pulses = std.ArrayList(Pulse).init(allocator);
|
||||||
|
defer pulses.deinit();
|
||||||
|
|
||||||
|
var pulse_count: u64 = 0;
|
||||||
|
pulse_loop: while (true) {
|
||||||
|
pulse_count += 1;
|
||||||
|
|
||||||
|
try pulses.append(Pulse{
|
||||||
|
.is_high = false,
|
||||||
|
.to = counter.input,
|
||||||
|
.from = input_module,
|
||||||
|
});
|
||||||
|
|
||||||
|
while (pulses.items.len > 0) {
|
||||||
|
const pulse: Pulse = pulses.orderedRemove(0);
|
||||||
|
|
||||||
|
if (pulse.to == counter.output) {
|
||||||
|
if (!pulse.is_high) break :pulse_loop;
|
||||||
|
} else {
|
||||||
|
try simulate_pulse(&pulses, machine_state, pulse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pulse_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lcm(a: u64, b: u64) u64 {
|
||||||
|
return a * b / std.math.gcd(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parse_input(allocator, input.lines);
|
||||||
@ -308,7 +483,19 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
var machine_state = try MachineState.init(allocator, parsed.modules.items);
|
var machine_state = try MachineState.init(allocator, parsed.modules.items);
|
||||||
defer machine_state.deinit();
|
defer machine_state.deinit();
|
||||||
|
|
||||||
return .{ .uint = 0 };
|
const broadcaster_idx = machine_state.get_name_idx(try NameString.fromSlice("broadcaster")).?;
|
||||||
|
const rx_idx = machine_state.get_name_idx(try NameString.fromSlice("rx")).?;
|
||||||
|
|
||||||
|
const analysis = try analyze_machine(allocator, machine_state, broadcaster_idx, rx_idx);
|
||||||
|
defer analysis.deinit();
|
||||||
|
|
||||||
|
var answer: u64 = 1;
|
||||||
|
for (analysis.counters) |counter| {
|
||||||
|
const cycle_size = try find_counter_cycle_size(allocator, &machine_state, counter, broadcaster_idx);
|
||||||
|
answer = lcm(answer, cycle_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .uint = answer };
|
||||||
}
|
}
|
||||||
|
|
||||||
const example_input1 = [_][]const u8{
|
const example_input1 = [_][]const u8{
|
||||||
|
Loading…
Reference in New Issue
Block a user