day20: problem 1 solution
This commit is contained in:
parent
cdfecf821c
commit
877101f9a2
118
20/Cargo.lock
generated
Normal file
118
20/Cargo.lock
generated
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day20"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"lazy-regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
|
||||||
|
dependencies = [
|
||||||
|
"lazy-regex-proc_macros",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex-proc_macros"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
9
20/Cargo.toml
Normal file
9
20/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "day20"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy-regex = "3.1.0"
|
58
20/input
Normal file
58
20/input
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
%hm -> cr
|
||||||
|
%qc -> nd
|
||||||
|
&dh -> rm
|
||||||
|
%ph -> zz
|
||||||
|
%ps -> kc, dt
|
||||||
|
%qb -> dt
|
||||||
|
%jl -> vt, tb
|
||||||
|
%fh -> dm, gr
|
||||||
|
broadcaster -> np, mg, vd, xr
|
||||||
|
%zz -> sq
|
||||||
|
&rm -> rx
|
||||||
|
%nd -> br
|
||||||
|
%nx -> vr, vt
|
||||||
|
%qf -> dt, dv
|
||||||
|
%np -> xm, ph
|
||||||
|
%dm -> nf, gr
|
||||||
|
%sq -> kj
|
||||||
|
%bv -> fp, xm
|
||||||
|
%br -> kt
|
||||||
|
%mg -> dz, gr
|
||||||
|
&dt -> vd, dv, dh, hm, ks, hd, kq
|
||||||
|
%ks -> qf
|
||||||
|
&qd -> rm
|
||||||
|
%xr -> vt, rn
|
||||||
|
%vr -> tg, vt
|
||||||
|
%lc -> xm
|
||||||
|
%tq -> gr, fh
|
||||||
|
%cr -> kq, dt
|
||||||
|
%vd -> dt, ks
|
||||||
|
%tb -> nx
|
||||||
|
%dz -> gr, fd
|
||||||
|
&gr -> dp, mg, fd, qn
|
||||||
|
%nf -> gr
|
||||||
|
%dv -> hm
|
||||||
|
%qj -> lc, xm
|
||||||
|
%kc -> dt, gf
|
||||||
|
%gf -> dt, qb
|
||||||
|
%vh -> xm, sv
|
||||||
|
%sr -> vt
|
||||||
|
%fp -> qg, xm
|
||||||
|
%kj -> vh
|
||||||
|
%pc -> tq, gr
|
||||||
|
%kq -> hd
|
||||||
|
%xd -> xg, gr
|
||||||
|
%tg -> sr, vt
|
||||||
|
&bb -> rm
|
||||||
|
%rn -> vt, qc
|
||||||
|
%hd -> ps
|
||||||
|
%qg -> xm, qj
|
||||||
|
&dp -> rm
|
||||||
|
%qn -> pc
|
||||||
|
%kt -> jl
|
||||||
|
%sv -> bv
|
||||||
|
&vt -> bb, nd, qc, xr, br, tb, kt
|
||||||
|
%fd -> mx
|
||||||
|
&xm -> zz, sv, sq, ph, kj, np, qd
|
||||||
|
%xg -> gr, qn
|
||||||
|
%mx -> gr, xd
|
304
20/src/main.rs
Normal file
304
20/src/main.rs
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
use std::collections::{BinaryHeap, HashMap, VecDeque};
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufRead, BufReader, Lines};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use lazy_regex::lazy_regex;
|
||||||
|
|
||||||
|
// BOILERPLATE
|
||||||
|
type InputIter = Lines<BufReader<File>>;
|
||||||
|
|
||||||
|
fn get_input() -> InputIter {
|
||||||
|
let f = File::open("input").unwrap();
|
||||||
|
let br = BufReader::new(f);
|
||||||
|
br.lines()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let start = Instant::now();
|
||||||
|
let ans1 = problem1(get_input());
|
||||||
|
let duration = start.elapsed();
|
||||||
|
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let ans2 = problem2(get_input());
|
||||||
|
let duration = start.elapsed();
|
||||||
|
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
|
||||||
|
}
|
||||||
|
|
||||||
|
// PARSE
|
||||||
|
|
||||||
|
const MODULE_PATTERN: lazy_regex::Lazy<lazy_regex::Regex> = lazy_regex!("^([%&]?)([a-z]+) -> ([a-z, ]+)$");
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum PulseType {
|
||||||
|
Low,
|
||||||
|
High,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for PulseType {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
match value {
|
||||||
|
true => PulseType::High,
|
||||||
|
false => PulseType::Low,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PulseType> for bool {
|
||||||
|
fn from(value: PulseType) -> Self {
|
||||||
|
match value {
|
||||||
|
PulseType::High => true,
|
||||||
|
PulseType::Low => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PulseType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PulseType::Low => f.write_str("low"),
|
||||||
|
PulseType::High => f.write_str("high"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum ModuleKind {
|
||||||
|
FlipFlop,
|
||||||
|
Conjunction,
|
||||||
|
Broadcast,
|
||||||
|
Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Module {
|
||||||
|
name: String,
|
||||||
|
kind: Option<ModuleKind>,
|
||||||
|
inputs: Vec<String>,
|
||||||
|
outputs: Vec<String>,
|
||||||
|
on: bool,
|
||||||
|
last_input_states: HashMap<String, PulseType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
fn new(name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
kind: None,
|
||||||
|
inputs: Vec::new(),
|
||||||
|
outputs: Vec::new(),
|
||||||
|
on: false,
|
||||||
|
last_input_states: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn with_kind(name: String, kind: ModuleKind) -> Self {
|
||||||
|
let mut m = Self::new(name);
|
||||||
|
m.kind = Some(kind);
|
||||||
|
m
|
||||||
|
}
|
||||||
|
fn and_destinations(mut self, destinations: &str) -> Self {
|
||||||
|
println!("{} destinations: {}", self.name, destinations);
|
||||||
|
for dest in destinations.split(", ") {
|
||||||
|
println!(" {}", dest);
|
||||||
|
self.outputs.push(dest.into());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn and_source(mut self, source: String) -> Self {
|
||||||
|
self.add_source(source);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn add_source(&mut self, source: String) {
|
||||||
|
self.inputs.push(source.clone());
|
||||||
|
self.last_input_states.insert(source, PulseType::Low);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Module {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
println!("s: {}", s);
|
||||||
|
let Some(re_result) = MODULE_PATTERN.captures(s) else {
|
||||||
|
panic!("unparseable module: {}", s);
|
||||||
|
};
|
||||||
|
let (_, [kind, name, destinations]) = re_result.extract();
|
||||||
|
if name == "broadcaster" {
|
||||||
|
return Module::with_kind(name.into(), ModuleKind::Broadcast).and_destinations(destinations);
|
||||||
|
};
|
||||||
|
match kind {
|
||||||
|
"%" => Module::with_kind(name.into(), ModuleKind::FlipFlop).and_destinations(destinations),
|
||||||
|
"&" => Module::with_kind(name.into(), ModuleKind::Conjunction).and_destinations(destinations),
|
||||||
|
_ => panic!("invalid module kind {}", kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Job {
|
||||||
|
signal: PulseType,
|
||||||
|
targets: Vec<String>,
|
||||||
|
from: String,
|
||||||
|
priority: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Job {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.priority == other.priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for Job {}
|
||||||
|
|
||||||
|
impl PartialOrd for Job {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
other.priority.partial_cmp(&self.priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Job {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.partial_cmp(other).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Machine {
|
||||||
|
modules: HashMap<String, Module>,
|
||||||
|
work_queue: BinaryHeap<Job>,
|
||||||
|
count_low: u64,
|
||||||
|
count_high: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BufRead> From<Lines<T>> for Machine {
|
||||||
|
fn from(mut lines: Lines<T>) -> Self {
|
||||||
|
let mut modules = HashMap::from([(
|
||||||
|
"button".into(),
|
||||||
|
Module::with_kind("button".into(), ModuleKind::Button).and_destinations("broadcaster"),
|
||||||
|
)]);
|
||||||
|
while let Some(Ok(line)) = lines.next() {
|
||||||
|
let mut module = Module::from(line.as_str());
|
||||||
|
for output in &module.outputs {
|
||||||
|
if let Some(output_mod) = modules.get_mut(output) {
|
||||||
|
// already exists, push to input list
|
||||||
|
output_mod.add_source(module.name.to_owned());
|
||||||
|
} else {
|
||||||
|
// forward declaration of 'unknown' modules
|
||||||
|
modules.insert(
|
||||||
|
output.clone(),
|
||||||
|
Module::new(output.clone()).and_source(module.name.clone()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(existing_mod) = modules.get(&module.name) {
|
||||||
|
module.inputs = existing_mod.inputs.clone(); // might have been pre-prepared, the rest we take ours
|
||||||
|
module.last_input_states = existing_mod.last_input_states.clone();
|
||||||
|
}
|
||||||
|
modules.insert(module.name.clone(), module);
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine {
|
||||||
|
modules,
|
||||||
|
work_queue: BinaryHeap::new(),
|
||||||
|
count_low: 0, count_high: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Machine {
|
||||||
|
fn press_button(&mut self) {
|
||||||
|
self.send_pulse(PulseType::Low, "broadcaster", "button", 1);
|
||||||
|
}
|
||||||
|
fn send_pulse(&mut self, signal: PulseType, target: &str, from: &str, priority: usize) {
|
||||||
|
// count pulse when it is received
|
||||||
|
match signal {
|
||||||
|
PulseType::Low => self.count_low += 1,
|
||||||
|
PulseType::High => self.count_high += 1,
|
||||||
|
};
|
||||||
|
match self.modules[target].kind {
|
||||||
|
Some(ModuleKind::Button) | Some(ModuleKind::Broadcast) => {
|
||||||
|
self.send_all_outputs(PulseType::Low, target, priority)
|
||||||
|
}
|
||||||
|
Some(ModuleKind::FlipFlop) => match signal {
|
||||||
|
PulseType::High => (),
|
||||||
|
PulseType::Low => {
|
||||||
|
let new_state = !self.modules.get_mut(target).unwrap().on;
|
||||||
|
self.modules.get_mut(target).unwrap().on = new_state;
|
||||||
|
self.send_all_outputs(new_state.into(), target, priority)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(ModuleKind::Conjunction) => {
|
||||||
|
self.modules
|
||||||
|
.get_mut(target)
|
||||||
|
.unwrap()
|
||||||
|
.last_input_states
|
||||||
|
.insert(from.to_owned(), signal);
|
||||||
|
if self.modules[target]
|
||||||
|
.last_input_states
|
||||||
|
.values()
|
||||||
|
.all(|state| *state == PulseType::High)
|
||||||
|
{
|
||||||
|
self.send_all_outputs(PulseType::Low, target, priority)
|
||||||
|
} else {
|
||||||
|
self.send_all_outputs(PulseType::High, target, priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn send_all_outputs(&mut self, signal: PulseType, from: &str, priority: usize) {
|
||||||
|
self.work_queue.push(Job {
|
||||||
|
signal,
|
||||||
|
targets: self.modules[from].outputs.iter().map(|op| op.to_owned()).collect(),
|
||||||
|
from: from.to_owned(),
|
||||||
|
priority: priority + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn run(&mut self) {
|
||||||
|
while let Some(job) = self.work_queue.pop() {
|
||||||
|
for target in job.targets {
|
||||||
|
self.send_pulse(job.signal, &target, &job.from, job.priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 1 solution
|
||||||
|
const PROBLEM1_ITERATIONS: usize = 1000;
|
||||||
|
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
|
let mut machine = Machine::from(input);
|
||||||
|
println!("{:?}", machine);
|
||||||
|
for _ in 0..PROBLEM1_ITERATIONS {
|
||||||
|
machine.press_button();
|
||||||
|
machine.run();
|
||||||
|
}
|
||||||
|
machine.count_low * machine.count_high
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 2 solution
|
||||||
|
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::*;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
const EXAMPLE: &str = &"broadcaster -> a
|
||||||
|
%a -> inv, con
|
||||||
|
&inv -> b
|
||||||
|
%b -> con
|
||||||
|
&con -> output";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn problem1_example() {
|
||||||
|
let c = Cursor::new(EXAMPLE);
|
||||||
|
assert_eq!(problem1(c.lines()), 11687500);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn problem2_example() {
|
||||||
|
let c = Cursor::new(EXAMPLE);
|
||||||
|
assert_eq!(problem2(c.lines()), 0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user