From 8e277d86f54dd30f748030a507a21f6e75c652c0 Mon Sep 17 00:00:00 2001 From: Philippe Zwietering Date: Wed, 29 Nov 2023 15:08:10 +0100 Subject: [PATCH] Finally finished the puzzle (aoc day 7 2015); I firmly believe that the circuit can do a whole lot more than what is described in the puzzle text --- advent_of_code/2015/7/Cargo.toml | 10 + advent_of_code/2015/7/input.txt | 339 ++++++++++++++++++++++++++++++ advent_of_code/2015/7/src/main.rs | 329 +++++++++++++++++++++++++++++ advent_of_code/2015/7/test.txt | 8 + 4 files changed, 686 insertions(+) create mode 100644 advent_of_code/2015/7/Cargo.toml create mode 100644 advent_of_code/2015/7/input.txt create mode 100644 advent_of_code/2015/7/src/main.rs create mode 100644 advent_of_code/2015/7/test.txt diff --git a/advent_of_code/2015/7/Cargo.toml b/advent_of_code/2015/7/Cargo.toml new file mode 100644 index 0000000..6a19b18 --- /dev/null +++ b/advent_of_code/2015/7/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "main" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nom = "7.1.3" +petgraph = "0.6.4" diff --git a/advent_of_code/2015/7/input.txt b/advent_of_code/2015/7/input.txt new file mode 100644 index 0000000..69971ac --- /dev/null +++ b/advent_of_code/2015/7/input.txt @@ -0,0 +1,339 @@ +NOT dq -> dr +kg OR kf -> kh +ep OR eo -> eq +44430 -> b +NOT gs -> gt +dd OR do -> dp +eg AND ei -> ej +y AND ae -> ag +jx AND jz -> ka +lf RSHIFT 2 -> lg +z AND aa -> ac +dy AND ej -> el +bj OR bi -> bk +kk RSHIFT 3 -> km +NOT cn -> co +gn AND gp -> gq +cq AND cs -> ct +eo LSHIFT 15 -> es +lg OR lm -> ln +dy OR ej -> ek +NOT di -> dj +1 AND fi -> fj +kf LSHIFT 15 -> kj +NOT jy -> jz +NOT ft -> fu +fs AND fu -> fv +NOT hr -> hs +ck OR cl -> cm +jp RSHIFT 5 -> js +iv OR jb -> jc +is OR it -> iu +ld OR le -> lf +NOT fc -> fd +NOT dm -> dn +bn OR by -> bz +aj AND al -> am +cd LSHIFT 15 -> ch +jp AND ka -> kc +ci OR ct -> cu +gv AND gx -> gy +de AND dk -> dm +x RSHIFT 5 -> aa +et RSHIFT 2 -> eu +x RSHIFT 1 -> aq +ia OR ig -> ih +bk LSHIFT 1 -> ce +y OR ae -> af +NOT ca -> cb +e AND f -> h +ia AND ig -> ii +ck AND cl -> cn +NOT jh -> ji +z OR aa -> ab +1 AND en -> eo +ib AND ic -> ie +NOT eh -> ei +iy AND ja -> jb +NOT bb -> bc +ha OR gz -> hb +1 AND cx -> cy +NOT ax -> ay +ev OR ew -> ex +bn RSHIFT 2 -> bo +er OR es -> et +eu OR fa -> fb +jp OR ka -> kb +ea AND eb -> ed +k AND m -> n +et RSHIFT 3 -> ev +et RSHIFT 5 -> ew +hz RSHIFT 1 -> is +ki OR kj -> kk +NOT h -> i +lv LSHIFT 15 -> lz +as RSHIFT 1 -> bl +hu LSHIFT 15 -> hy +iw AND ix -> iz +lf RSHIFT 1 -> ly +fp OR fv -> fw +1 AND am -> an +ap LSHIFT 1 -> bj +u LSHIFT 1 -> ao +b RSHIFT 5 -> f +jq AND jw -> jy +iu RSHIFT 3 -> iw +ih AND ij -> ik +NOT iz -> ja +de OR dk -> dl +iu OR jf -> jg +as AND bd -> bf +b RSHIFT 3 -> e +jq OR jw -> jx +iv AND jb -> jd +cg OR ch -> ci +iu AND jf -> jh +lx -> a +1 AND cc -> cd +ly OR lz -> ma +NOT el -> em +1 AND bh -> bi +fb AND fd -> fe +lf OR lq -> lr +bn RSHIFT 3 -> bp +bn AND by -> ca +af AND ah -> ai +cf LSHIFT 1 -> cz +dw OR dx -> dy +gj AND gu -> gw +jg AND ji -> jj +jr OR js -> jt +bl OR bm -> bn +gj RSHIFT 2 -> gk +cj OR cp -> cq +gj OR gu -> gv +b OR n -> o +o AND q -> r +bi LSHIFT 15 -> bm +dy RSHIFT 1 -> er +cu AND cw -> cx +iw OR ix -> iy +hc OR hd -> he +0 -> c +db OR dc -> dd +kk RSHIFT 2 -> kl +eq LSHIFT 1 -> fk +dz OR ef -> eg +NOT ed -> ee +lw OR lv -> lx +fw AND fy -> fz +dz AND ef -> eh +jp RSHIFT 3 -> jr +lg AND lm -> lo +ci RSHIFT 2 -> cj +be AND bg -> bh +lc LSHIFT 1 -> lw +hm AND ho -> hp +jr AND js -> ju +1 AND io -> ip +cm AND co -> cp +ib OR ic -> id +NOT bf -> bg +fo RSHIFT 5 -> fr +ip LSHIFT 15 -> it +jt AND jv -> jw +jc AND je -> jf +du OR dt -> dv +NOT fx -> fy +aw AND ay -> az +ge LSHIFT 15 -> gi +NOT ak -> al +fm OR fn -> fo +ff AND fh -> fi +ci RSHIFT 5 -> cl +cz OR cy -> da +NOT ey -> ez +NOT ju -> jv +NOT ls -> lt +kk AND kv -> kx +NOT ii -> ij +kl AND kr -> kt +jk LSHIFT 15 -> jo +e OR f -> g +NOT bs -> bt +hi AND hk -> hl +hz OR ik -> il +ek AND em -> en +ao OR an -> ap +dv LSHIFT 1 -> ep +an LSHIFT 15 -> ar +fo RSHIFT 1 -> gh +NOT im -> in +kk RSHIFT 1 -> ld +hw LSHIFT 1 -> iq +ec AND ee -> ef +hb LSHIFT 1 -> hv +kb AND kd -> ke +x AND ai -> ak +dd AND do -> dq +aq OR ar -> as +iq OR ip -> ir +dl AND dn -> do +iu RSHIFT 5 -> ix +as OR bd -> be +NOT go -> gp +fk OR fj -> fl +jm LSHIFT 1 -> kg +NOT cv -> cw +dp AND dr -> ds +dt LSHIFT 15 -> dx +et RSHIFT 1 -> fm +dy RSHIFT 3 -> ea +fp AND fv -> fx +NOT p -> q +dd RSHIFT 2 -> de +eu AND fa -> fc +ba AND bc -> bd +dh AND dj -> dk +lr AND lt -> lu +he RSHIFT 1 -> hx +ex AND ez -> fa +df OR dg -> dh +fj LSHIFT 15 -> fn +NOT kx -> ky +gk OR gq -> gr +dy RSHIFT 2 -> dz +gh OR gi -> gj +lj AND ll -> lm +x OR ai -> aj +bz AND cb -> cc +1 AND lu -> lv +as RSHIFT 3 -> au +ce OR cd -> cf +il AND in -> io +dd RSHIFT 1 -> dw +NOT lo -> lp +c LSHIFT 1 -> t +dd RSHIFT 3 -> df +dd RSHIFT 5 -> dg +lh AND li -> lk +lf RSHIFT 5 -> li +dy RSHIFT 5 -> eb +NOT kt -> ku +at OR az -> ba +x RSHIFT 3 -> z +NOT lk -> ll +lb OR la -> lc +1 AND r -> s +lh OR li -> lj +ln AND lp -> lq +kk RSHIFT 5 -> kn +ea OR eb -> ec +ci AND ct -> cv +b RSHIFT 2 -> d +jp RSHIFT 1 -> ki +NOT cr -> cs +NOT jd -> je +jp RSHIFT 2 -> jq +jn OR jo -> jp +lf RSHIFT 3 -> lh +1 AND ds -> dt +lf AND lq -> ls +la LSHIFT 15 -> le +NOT fg -> fh +at AND az -> bb +au AND av -> ax +kw AND ky -> kz +v OR w -> x +kk OR kv -> kw +ks AND ku -> kv +kh LSHIFT 1 -> lb +1 AND kz -> la +NOT kc -> kd +x RSHIFT 2 -> y +et OR fe -> ff +et AND fe -> fg +NOT ac -> ad +jl OR jk -> jm +1 AND jj -> jk +bn RSHIFT 1 -> cg +NOT kp -> kq +ci RSHIFT 3 -> ck +ev AND ew -> ey +1 AND ke -> kf +cj AND cp -> cr +ir LSHIFT 1 -> jl +NOT gw -> gx +as RSHIFT 2 -> at +iu RSHIFT 1 -> jn +cy LSHIFT 15 -> dc +hg OR hh -> hi +ci RSHIFT 1 -> db +au OR av -> aw +km AND kn -> kp +gj RSHIFT 1 -> hc +iu RSHIFT 2 -> iv +ab AND ad -> ae +da LSHIFT 1 -> du +NOT bw -> bx +km OR kn -> ko +ko AND kq -> kr +bv AND bx -> by +kl OR kr -> ks +1 AND ht -> hu +df AND dg -> di +NOT ag -> ah +d OR j -> k +d AND j -> l +b AND n -> p +gf OR ge -> gg +gg LSHIFT 1 -> ha +bn RSHIFT 5 -> bq +bo OR bu -> bv +1 AND gy -> gz +s LSHIFT 15 -> w +NOT ie -> if +as RSHIFT 5 -> av +bo AND bu -> bw +hz AND ik -> im +bp AND bq -> bs +b RSHIFT 1 -> v +NOT l -> m +bp OR bq -> br +g AND i -> j +br AND bt -> bu +t OR s -> u +hz RSHIFT 5 -> ic +gk AND gq -> gs +fl LSHIFT 1 -> gf +he RSHIFT 3 -> hg +gz LSHIFT 15 -> hd +hf OR hl -> hm +1 AND gd -> ge +fo OR fz -> ga +id AND if -> ig +fo AND fz -> gb +gr AND gt -> gu +he OR hp -> hq +fq AND fr -> ft +ga AND gc -> gd +fo RSHIFT 2 -> fp +gl OR gm -> gn +hg AND hh -> hj +NOT hn -> ho +gl AND gm -> go +he RSHIFT 5 -> hh +NOT gb -> gc +hq AND hs -> ht +hz RSHIFT 3 -> ib +hz RSHIFT 2 -> ia +fq OR fr -> fs +hx OR hy -> hz +he AND hp -> hr +gj RSHIFT 5 -> gm +hf AND hl -> hn +hv OR hu -> hw +NOT hj -> hk +gj RSHIFT 3 -> gl +fo RSHIFT 3 -> fq +he RSHIFT 2 -> hf \ No newline at end of file diff --git a/advent_of_code/2015/7/src/main.rs b/advent_of_code/2015/7/src/main.rs new file mode 100644 index 0000000..39908d5 --- /dev/null +++ b/advent_of_code/2015/7/src/main.rs @@ -0,0 +1,329 @@ +use core::fmt; +use std::{collections::HashMap, iter::zip}; + +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::{self, alpha1, multispace1}, + multi::separated_list1, + sequence::tuple, + IResult, Parser, +}; + +#[derive(Debug, Clone)] +enum Instruction { + Assign(u16, String), + Connect(String, String), + Not(String, String), + And(String, String, String), + OneAnd(String, String), + Or(String, String, String), + Lshift(u16, String, String), + Rshift(u16, String, String), +} + +impl fmt::Display for Instruction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Instruction::Assign(value, wire) => write!(f, "{} -> {}", value, wire), + Instruction::Connect(source, target) => write!(f, "{} -> {}", source, target), + Instruction::Not(source, target) => write!(f, "NOT {} -> {}", source, target), + Instruction::And(source1, source2, target) => { + write!(f, "{} AND {} -> {}", source1, source2, target) + } + Instruction::OneAnd(source, target) => write!(f, "1 AND {} -> {}", source, target), + Instruction::Or(source1, source2, target) => { + write!(f, "{} OR {} -> {}", source1, source2, target) + } + Instruction::Lshift(value, source, target) => { + write!(f, "{} LSHIFT {} -> {}", source, value, target) + } + Instruction::Rshift(value, source, target) => { + write!(f, "{} RSHIFT {} -> {}", source, value, target) + } + } + } +} + +#[derive(Debug, Clone, Copy)] +enum Port { + Noop, + Not, + And, + Or, + Lshift(u16), + Rshift(u16), +} + +impl fmt::Display for Port { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Port::Noop => write!(f, "Noop"), + Port::Not => write!(f, "Not"), + Port::And => write!(f, "And"), + Port::Or => write!(f, "Or"), + Port::Lshift(_) => write!(f, "Lshift"), + Port::Rshift(_) => write!(f, "Rshift"), + } + } +} + +fn parse_input(input: &str) -> IResult<&str, Vec> { + let (input, result) = separated_list1( + multispace1, + alt(( + tuple((complete::u16::<&str, _>, tag(" -> "), alpha1)) + .map(|(val, _, target)| Instruction::Assign(val, target.to_string())), + tuple((alpha1::<&str, _>, tag(" -> "), alpha1)).map(|(source, _, target)| { + Instruction::Connect(source.to_string(), target.to_string()) + }), + tuple((tag("NOT "), alpha1::<&str, _>, tag(" -> "), alpha1)).map( + |(_, source, _, target)| Instruction::Not(source.to_string(), target.to_string()), + ), + tuple((alpha1::<&str, _>, tag(" AND "), alpha1, tag(" -> "), alpha1)).map( + |(source_1, _, source_2, _, target)| { + Instruction::And( + source_1.to_string(), + source_2.to_string(), + target.to_string(), + ) + }, + ), + tuple((tag("1 AND "), alpha1::<&str, _>, tag(" -> "), alpha1)).map( + |(_, source, _, target)| { + Instruction::OneAnd(source.to_string(), target.to_string()) + }, + ), + tuple((alpha1::<&str, _>, tag(" OR "), alpha1, tag(" -> "), alpha1)).map( + |(source_1, _, source_2, _, target)| { + Instruction::Or( + source_1.to_string(), + source_2.to_string(), + target.to_string(), + ) + }, + ), + tuple(( + alpha1::<&str, _>, + tag(" LSHIFT "), + complete::u16, + tag(" -> "), + alpha1, + )) + .map(|(source, _, shift_amount, _, target)| { + Instruction::Lshift(shift_amount, source.to_string(), target.to_string()) + }), + tuple(( + alpha1::<&str, _>, + tag(" RSHIFT "), + complete::u16, + tag(" -> "), + alpha1, + )) + .map(|(source, _, shift_amount, _, target)| { + Instruction::Rshift(shift_amount, source.to_string(), target.to_string()) + }), + )), + )(input)?; + + Ok((input, result)) +} + +type Node = (Port, Vec, Option); + +fn solve_first(instructions: &[Instruction], output_wire: &str) -> (u16, HashMap) { + // Because the graph is centered around the edges instead of nodes, we have to go through a lot of gymnastics to build it + let mut signals = HashMap::::new(); + + // For OneAnd operator + signals.insert("one".to_string(), (Port::Noop, vec![], Some(1))); + + for instruction in instructions { + match instruction { + Instruction::Assign(value, wire) => { + signals.insert(wire.to_owned(), (Port::Noop, vec![], Some(*value))); + } + Instruction::Connect(source, target) => { + signals.insert( + target.to_owned(), + (Port::Noop, vec![source.to_owned()], None), + ); + } + // We could already preprocess a bit by calculating the result if we know the input, but that might make it overly complicated a this point + // Instead, we do the actual traversal and calculations later + Instruction::Not(source, target) => { + signals.insert( + target.to_owned(), + (Port::Not, vec![source.to_owned()], None), + ); + } + Instruction::And(source_1, source_2, target) => { + signals.insert( + target.to_owned(), + ( + Port::And, + vec![source_1.to_owned(), source_2.to_owned()], + None, + ), + ); + } + Instruction::OneAnd(source, target) => { + signals.insert( + target.to_owned(), + (Port::And, vec!["one".to_string(), source.to_owned()], None), + ); + } + Instruction::Or(source_1, source_2, target) => { + signals.insert( + target.to_owned(), + ( + Port::Or, + vec![source_1.to_owned(), source_2.to_owned()], + None, + ), + ); + } + Instruction::Lshift(shift, source, target) => { + signals.insert( + target.to_owned(), + (Port::Lshift(*shift), vec![source.to_owned()], None), + ); + } + Instruction::Rshift(shift, source, target) => { + signals.insert( + target.to_owned(), + (Port::Rshift(*shift), vec![source.to_owned()], None), + ); + } + }; + } + + let mut edges_to_calc = vec![output_wire.to_string()]; + + while !edges_to_calc.is_empty() { + let edge_to_calc = edges_to_calc.pop().unwrap(); + + if let Some(signal) = signals.get(&edge_to_calc) { + match signal { + (port, incoming_wires, None) => { + let incoming_signals: Vec> = incoming_wires + .iter() + .map(|x| match signals.get(x).unwrap() { + (_, _, None) => None, + (_, _, s) => s.to_owned(), + }) + .collect(); + + if incoming_signals.iter().any(|s| s.is_none()) { + edges_to_calc.push(edge_to_calc); + + for (s, w) in zip(incoming_signals, incoming_wires) { + if s.is_none() { + edges_to_calc.push(w.to_owned()); + } + } + } else { + match port { + Port::Noop => { + assert_eq!(incoming_signals.len(), 1); + + signals.insert( + edge_to_calc.to_owned(), + (*port, incoming_wires.to_owned(), incoming_signals[0]), + ); + } + Port::Not => { + assert_eq!(incoming_signals.len(), 1); + + signals.insert( + edge_to_calc.to_owned(), + ( + *port, + incoming_wires.to_owned(), + Some(!incoming_signals[0].unwrap()), + ), + ); + } + Port::And => { + assert_eq!(incoming_signals.len(), 2); + + signals.insert( + edge_to_calc.to_owned(), + ( + *port, + incoming_wires.to_owned(), + Some( + incoming_signals[0].unwrap() + & incoming_signals[1].unwrap(), + ), + ), + ); + } + Port::Or => { + assert_eq!(incoming_signals.len(), 2); + + signals.insert( + edge_to_calc.to_owned(), + ( + *port, + incoming_wires.to_owned(), + Some( + incoming_signals[0].unwrap() + | incoming_signals[1].unwrap(), + ), + ), + ); + } + Port::Lshift(shift) => { + assert_eq!(incoming_signals.len(), 1); + + signals.insert( + edge_to_calc.to_owned(), + ( + *port, + incoming_wires.to_owned(), + Some(incoming_signals[0].unwrap() << shift), + ), + ); + } + Port::Rshift(shift) => { + assert_eq!(incoming_signals.len(), 1); + + signals.insert( + edge_to_calc.to_owned(), + ( + *port, + incoming_wires.to_owned(), + Some(incoming_signals[0].unwrap() >> shift), + ), + ); + } + } + } + } + (_, _, Some(_)) => { /* do nothing */ } + } + } else { + unreachable!(); + } + } + + let result = signals.get(&output_wire.to_string()).unwrap().to_owned(); + + match result { + (_, _, None) => (0, HashMap::new()), + (_, _, Some(s)) => (s, signals.to_owned()), + } +} + +fn main() { + println!("Hello, this is Patrick!"); + + let input_txt = include_str!("../input.txt"); + + let (_, instructions) = parse_input(input_txt).unwrap(); + + let (first_result, _) = solve_first(&instructions[..], "a"); + + println!("The provided signal at wire 'a' is {}", first_result); +} diff --git a/advent_of_code/2015/7/test.txt b/advent_of_code/2015/7/test.txt new file mode 100644 index 0000000..febb2a0 --- /dev/null +++ b/advent_of_code/2015/7/test.txt @@ -0,0 +1,8 @@ +123 -> x +456 -> y +x AND y -> d +x OR y -> e +x LSHIFT 2 -> f +y RSHIFT 2 -> g +NOT x -> h +NOT y -> i \ No newline at end of file