solve day 20 part 1

This commit is contained in:
Rokas Puzonas 2024-04-28 22:26:18 +03:00
parent ff8924991c
commit eae81ce631
2 changed files with 338 additions and 0 deletions

336
src/day20.zig Normal file
View File

@ -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);
}

View File

@ -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");
}