From 1910caab51f69d5a26bde666ed0817ca89a06020 Mon Sep 17 00:00:00 2001 From: Philippe Zwietering Date: Wed, 29 Nov 2023 15:50:26 +0100 Subject: [PATCH] If it works it works, the fugliest code ever hehe --- advent_of_code/2015/7/src/main.rs | 373 ++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) diff --git a/advent_of_code/2015/7/src/main.rs b/advent_of_code/2015/7/src/main.rs index 39908d5..674d5f1 100644 --- a/advent_of_code/2015/7/src/main.rs +++ b/advent_of_code/2015/7/src/main.rs @@ -316,6 +316,372 @@ fn solve_first(instructions: &[Instruction], output_wire: &str) -> (u16, HashMap } } +fn solve_second( + instructions: &[Instruction], + output_wire: &str, + override_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(); + + 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), + ); + } + }; + } + + signals.insert( + override_wire.to_string(), + (Port::Noop, vec![], Some(result.2.unwrap())), + ); + + 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!"); @@ -326,4 +692,11 @@ fn main() { let (first_result, _) = solve_first(&instructions[..], "a"); println!("The provided signal at wire 'a' is {}", first_result); + + let (second_result, _) = solve_second(&instructions[..], "a", "b"); + + println!( + "The signal at 'a' is {} when overriding 'b' with it and running it again", + { second_result } + ); }