From 6585356f0e978be4f991b7226e8b7fccc36f3a6d Mon Sep 17 00:00:00 2001 From: Jan-Bulthuis Date: Wed, 25 Dec 2024 05:53:45 +0100 Subject: [PATCH] day24 --- aoc_2024/src/day23.rs | 16 --- aoc_2024/src/day24.rs | 303 ++++++++++++++++++++++++++++++++++++++++++ aoc_2024/src/lib.rs | 2 + 3 files changed, 305 insertions(+), 16 deletions(-) create mode 100644 aoc_2024/src/day24.rs diff --git a/aoc_2024/src/day23.rs b/aoc_2024/src/day23.rs index c5b81ec..b48e115 100644 --- a/aoc_2024/src/day23.rs +++ b/aoc_2024/src/day23.rs @@ -145,22 +145,6 @@ fn expand_cliques(current: Vec, num: usize, adj: &[Vec]) -> Vec, coloring: &[i32]) { -// let mut q = HashSet::new(); -// let mut q_max = HashSet::new(); - -// while !r.is_empty() { -// let p = *r.iter().max_by_key(|node| coloring[**node]).unwrap(); -// r.remove(&p); -// if q.len() + coloring[p] > q_max.len() { -// q.insert(p); -// q.remove(p); -// } else { -// return; -// } -// } -// } - #[cfg(test)] mod tests { use super::*; diff --git a/aoc_2024/src/day24.rs b/aoc_2024/src/day24.rs new file mode 100644 index 0000000..b851d1e --- /dev/null +++ b/aoc_2024/src/day24.rs @@ -0,0 +1,303 @@ +use std::collections::VecDeque; + +use aoc_runner_derive::{aoc, aoc_generator}; +use hashbrown::{HashMap, HashSet}; +use nom::{ + bytes::complete::tag, + character::complete::{anychar, newline, one_of, space1, u64}, + multi::{fill, many1, separated_list0}, + sequence::{delimited, separated_pair}, + IResult, +}; + +type Line = [char; 3]; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Operation { + And, + Or, + Xor, +} + +#[derive(Debug, Clone, Copy)] +struct Gate { + operation: Operation, + in_0: Line, + in_1: Line, + out: Line, +} + +type Input = (Vec<(Line, u64)>, Vec); + +#[aoc_generator(day24)] +fn parse(input: &str) -> Input { + separated_pair( + separated_list0(newline, separated_pair(parse_line, tag(": "), u64)), + tag("\n\n"), + separated_list0(newline, parse_instruction), + )(input) + .unwrap() + .1 +} + +fn parse_line(input: &str) -> IResult<&str, [char; 3]> { + let mut buffer = ['0'; 3]; + let (input, _) = fill(anychar, &mut buffer)(input)?; + Ok((input, buffer)) +} + +fn parse_instruction(input: &str) -> IResult<&str, Gate> { + let (input, in_0) = parse_line(input)?; + let (input, operation) = delimited(space1, many1(one_of("ANDXOR")), space1)(input)?; + let (input, in_1) = parse_line(input)?; + let (input, _) = tag(" -> ")(input)?; + let (input, out) = parse_line(input)?; + match operation[0] { + 'A' => Ok(( + input, + Gate { + in_0, + in_1, + out, + operation: Operation::And, + }, + )), + 'O' => Ok(( + input, + Gate { + in_0, + in_1, + out, + operation: Operation::Or, + }, + )), + 'X' => Ok(( + input, + Gate { + in_0, + in_1, + out, + operation: Operation::Xor, + }, + )), + _ => unreachable!(), + } +} + +#[aoc(day24, part1)] +fn part1(input: &Input) -> usize { + let fixed = &input.0; + + let mut values = HashMap::new(); + + for (line, value) in fixed { + values.insert(line, *value); + } + + let mut gates = VecDeque::new(); + + for gate in &input.1 { + gates.push_back(gate); + } + + while let Some(gate) = gates.pop_front() { + if values.contains_key(&gate.in_0) && values.contains_key(&gate.in_1) { + let in_0 = values[&gate.in_0]; + let in_1 = values[&gate.in_1]; + let out = match gate.operation { + Operation::And => in_0 & in_1, + Operation::Or => in_0 | in_1, + Operation::Xor => in_0 ^ in_1, + }; + values.insert(&gate.out, out); + } else { + gates.push_back(gate); + } + } + + values + .into_iter() + .filter(|(line, value)| line[0] == 'z' && *value == 1) + .map(|(line, _)| { + 1 << line[1..3] + .iter() + .collect::() + .parse::() + .unwrap() + }) + .sum() +} + +#[aoc(day24, part2)] +fn part2(input: &Input) -> String { + let swaps: &[(Line, Line)] = &[ + (['h', 'm', 't'], ['z', '1', '8']), + (['b', 'f', 'q'], ['z', '2', '7']), + (['h', 'k', 'h'], ['z', '3', '1']), + (['f', 'j', 'p'], ['b', 'n', 'g']), + ]; + // let swaps: &[(Line, Line)] = &[]; + let mut gates = input.1.clone(); + for gate in &mut gates { + for (from, to) in swaps { + if gate.out == *from { + gate.out = *to; + } else if gate.out == *to { + gate.out = *from; + } + } + } + + for gate in &gates { + if gate.out[0] == 'z' && gate.operation != Operation::Xor { + println!("gate {:?} outputs to a z but is not xor", gate); + } + } + + let mut c = ['r', 'j', 'r']; + + for bit in 1..=44 { + let x = [ + 'x', + (bit as u8 / 10 + 48) as char, + (bit as u8 % 10 + 48) as char, + ]; + let y = [ + 'y', + (bit as u8 / 10 + 48) as char, + (bit as u8 % 10 + 48) as char, + ]; + + let xy_gates = find_gate(&gates, &x, &y); + + assert_eq!( + xy_gates.len(), + 2, + "there is not exactly two xy gates {:?}\nrelated gates: {:?}", + xy_gates, + find_debug_gate(&gates, &x, &y) + ); + + let line_xy_xor = xy_gates + .iter() + .find(|g| g.operation == Operation::Xor) + .unwrap() + .out; + + let line_xy_and = xy_gates + .iter() + .find(|g| g.operation == Operation::And) + .unwrap() + .out; + + let xyc_gates = find_gate(&gates, &line_xy_xor, &c); + + assert_eq!(xyc_gates.len(), 2, + "there is not exactly two output gates {:?}\nrelated gates: {:?}\nxy_and: {}\nxy_xor: {}", + xyc_gates, + find_debug_gate(&gates, &line_xy_xor, &c), + display_line(&line_xy_and), + display_line(&line_xy_xor), + ); + + let line_xyc_xor = xyc_gates + .iter() + .find(|g| g.operation == Operation::Xor) + .unwrap() + .out; + + let line_xyc_and = xyc_gates + .iter() + .find(|g| g.operation == Operation::And) + .unwrap() + .out; + + let xyco_gates = find_gate(&gates, &line_xyc_and, &line_xy_and); + + assert_eq!( + xyco_gates.len(), + 1, + "there is no or more than one output gate {:?}\nrelated gates: {:?}\nxy_and: {}\nxy_xor: {}\nxyc_and: {}\nxyc_xor: {}", + xyco_gates, + find_debug_gate(&gates, &line_xyc_and, &line_xy_and), + display_line(&line_xy_and), + display_line(&line_xy_xor), + display_line(&line_xyc_and), + display_line(&line_xyc_xor), + ); + + c = xyco_gates[0].out; + + println!( + "for bit {} z-output is {} and carry out is {}", + bit, + display_line(&line_xyc_xor), + display_line(&c) + ); + } + + let mut wires = swaps + .iter() + .flat_map(|(a, b)| [display_line(a), display_line(b)]) + .collect::>(); + + wires.sort(); + + wires.join(",") +} + +fn display_line(line: &Line) -> String { + line.iter().collect() +} + +fn find_gate(gates: &[Gate], in_0: &Line, in_1: &Line) -> Vec { + gates + .iter() + .filter(|gate| { + (gate.in_0 == *in_0 && gate.in_1 == *in_1) || (gate.in_0 == *in_1 && gate.in_1 == *in_0) + }) + .copied() + .collect() +} + +fn find_debug_gate(gates: &[Gate], in_0: &Line, in_1: &Line) -> Vec { + gates + .iter() + .filter(|gate| { + gate.in_0 == *in_0 || gate.in_1 == *in_1 || gate.in_0 == *in_1 || gate.in_1 == *in_0 + }) + .copied() + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_example() { + assert_eq!( + part1(&parse( + "x00: 1 +x01: 1 +x02: 1 +y00: 0 +y01: 1 +y02: 0 + +x00 AND y00 -> z00 +x01 XOR y01 -> z01 +x02 OR y02 -> z02" + )), + 4 + ); + } + + #[test] + fn part2_example() { + assert_eq!( + part2(&parse(include_str!("../input/2024/day24.txt").trim_end())), + "z00,z01,z02,z05" + ); + } +} diff --git a/aoc_2024/src/lib.rs b/aoc_2024/src/lib.rs index 5b52562..c0a80e3 100644 --- a/aoc_2024/src/lib.rs +++ b/aoc_2024/src/lib.rs @@ -1,3 +1,5 @@ +mod day24; +mod day23; mod day22; mod day21; mod day1;