From 9a43cb0f4a9a5efdea3ba3de0f3d831bec323e53 Mon Sep 17 00:00:00 2001 From: sepia Date: Fri, 27 Dec 2024 00:17:28 -0600 Subject: [PATCH] Day 24 (late) --- Cargo.lock | 67 +++++++++++++++ Cargo.toml | 1 + src/day24.rs | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +- 4 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 src/day24.rs diff --git a/Cargo.lock b/Cargo.lock index 596d447..f643a1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "itertools 0.13.0", "pest", "pest_derive", + "rand", "raylib", "rayon", "regex", @@ -129,6 +130,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.9.0" @@ -1085,6 +1092,15 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "prettyplease" version = "0.2.25" @@ -1113,6 +1129,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "raylib" version = "5.0.2" @@ -2027,6 +2073,27 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zerofrom" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index f60627f..9300416 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ dotenvy = "0.15.7" itertools = "0.13.0" pest = "2.7.15" pest_derive = "2.7.15" +rand = "0.8.5" raylib = "5.0.2" rayon = "1.10.0" regex = "1.11.1" diff --git a/src/day24.rs b/src/day24.rs new file mode 100644 index 0000000..673d41d --- /dev/null +++ b/src/day24.rs @@ -0,0 +1,229 @@ +use itertools::Itertools; +use std::collections::HashMap; + +pub fn day24() { + let (_values, mut connections) = input(&crate::input(24)); + + fn swap(connections: &mut Vec, a_name: &str, b_name: &str) { + let a = connections + .iter() + .position(|connection| &connection.output == a_name) + .unwrap(); + let b = connections + .iter() + .position(|connection| &connection.output == b_name) + .unwrap(); + connections[a].output = b_name.to_string(); + connections[b].output = a_name.to_string(); + } + swap(&mut connections, "z06", "vwr"); + swap(&mut connections, "z11", "tqm"); + swap(&mut connections, "z16", "kfs"); + swap(&mut connections, "hcm", "gfv"); + + fn wire_number(w: &str) -> u64 { + w.get(1..).unwrap().parse().unwrap() + } + + fn is_start_wire(w: &str) -> bool { + w.starts_with("x") || w.starts_with("y") + } + + fn are_start_wires(a: &str, b: &str) -> Option { + if !is_start_wire(a) || !is_start_wire(b) { + return None; + } + let a_n = wire_number(a); + let b_n = wire_number(b); + if a_n == b_n { + Some(a_n) + } else { + None + } + } + + let mut carry_out_wires: HashMap = HashMap::new(); + let mut second_carries: HashMap = HashMap::new(); + for i in 0..45 { + // The first sum wire goes xi XOR yi + let first_sum = connections.iter().find(|connection| { + are_start_wires(&connection.a, &connection.b) == Some(i) && connection.f == xor + }); + let first_sum = match first_sum { + Some(x) => &x.output, + None => panic!("First sum for {i} does not exist."), + }; + // The first carry wire goes xi AND yi + let first_carry = connections.iter().find(|connection| { + are_start_wires(&connection.a, &connection.b) == Some(i) && connection.f == and + }); + let first_carry = match first_carry { + Some(x) => &x.output, + None => panic!("First carry for {i} does not exist."), + }; + // If this is 0, the first carry is the carry_out and the first sum is the output (and skip the rest) + if i == 0 { + if wire_number(&first_sum) != i || !first_sum.starts_with("z") { + panic!("The first output wire is wrong."); + } + carry_out_wires.insert(0, first_carry.clone()); + continue; + } + // The output wire goes [first_sum] XOR [prev carry_out] -> zi + let prev_carry_out = carry_out_wires.get(&(i - 1)).unwrap(); + let output = &connections + .iter() + .find(|connection| { + let p1 = &connection.a == first_sum + && &connection.b == prev_carry_out + && connection.f == xor; + let p2 = &connection.b == first_sum + && &connection.a == prev_carry_out + && connection.f == xor; + p1 || p2 + }) + .expect(&format!("Couldn't find an output for {i}. Previous carry-out was {prev_carry_out} and first_sum was {first_sum}.")) + .output; + if !output.starts_with("z") || wire_number(&output) != i { + println!( + "The output for {i} should be z{i} but is instead {}", + output + ); + } + // The second carry wire goes [first_sum] AND [prev carry_out] + let second_carry = &connections + .iter() + .find(|connection| { + let p1 = &connection.a == first_sum + && &connection.b == prev_carry_out + && connection.f == and; + let p2 = &connection.b == first_sum + && &connection.a == prev_carry_out + && connection.f == and; + p1 || p2 + }) + .expect(&format!("Couldn't find a second carry for {i}.")) + .output; + second_carries.insert(i, second_carry.clone()); + // The carry_out goes [first_carry OR second_carry] + let carry_out = &connections + .iter() + .find(|connection| { + let p1 = &connection.a == first_carry + && &connection.b == second_carry + && connection.f == or; + let p2 = &connection.b == first_carry + && &connection.a == second_carry + && connection.f == or; + p1 || p2 + }) + .expect(&format!("Couldn't find a carry out for {i}.")) + .output; + carry_out_wires.insert(i, carry_out.clone()); + } + // Finally, make sure z45 is carry_out of 44 + if carry_out_wires.get(&44).unwrap() != "z45" { + println!( + "The final carry out should be z45 but is instead {}", + carry_out_wires.get(&44).unwrap() + ); + } +} + +fn run_system_with_inputs(connections: &Vec, x: u64, y: u64) -> u64 { + let mut values: HashMap = HashMap::new(); + for b in 0..45 { + values.insert(format!("x{:02}", b), ((1 << b) & x) != 0); + values.insert(format!("y{:02}", b), ((1 << b) & y) != 0); + } + run_system(&values, connections) +} + +fn run_system(values: &HashMap, connections: &Vec) -> u64 { + let mut values = values.clone(); + let mut connections = connections.clone(); + let mut remaining_connections = vec![]; + while !connections.is_empty() { + for connection in connections { + if values.contains_key(&connection.a) && values.contains_key(&connection.b) { + values.insert( + connection.output.to_string(), + (connection.f)(values[&connection.a], values[&connection.b]), + ); + } else { + remaining_connections.push(connection); + } + } + connections = remaining_connections; + remaining_connections = vec![]; + } + + let mut number: u64 = 0; + for (wire, value) in values.iter() { + if wire.starts_with("z") { + let place: u64 = wire.get(1..).unwrap().parse().unwrap(); + number = number | ((*value as u64) << place); + } + } + + number +} + +type Gate = fn(bool, bool) -> bool; + +#[derive(Clone)] +struct Connection { + a: String, + b: String, + f: Gate, + output: String, +} + +impl Connection { + fn new(a: String, b: String, f: Gate, output: String) -> Self { + Self { a, b, f, output } + } +} + +fn input(s: &str) -> (HashMap, Vec) { + let (values_input, gates_input) = s.split("\n\n").collect_tuple().unwrap(); + let mut values: HashMap = values_input + .lines() + .map(|line| { + let (name, value) = line.split(": ").collect_tuple().unwrap(); + let value = match value { + "0" => false, + "1" => true, + _ => panic!(), + }; + (name.to_string(), value) + }) + .collect(); + let mut connections: Vec = gates_input + .lines() + .map(|line| { + let parts: Vec<&str> = line.split(" ").collect(); + let (a, gate, b, c) = (parts[0], parts[1], parts[2], parts[4]); + let gate = match gate { + "AND" => and, + "OR" => or, + "XOR" => xor, + _ => panic!(), + }; + Connection::new(a.to_string(), b.to_string(), gate, c.to_string()) + }) + .collect(); + (values, connections) +} + +fn and(a: bool, b: bool) -> bool { + a && b +} + +fn or(a: bool, b: bool) -> bool { + a || b +} + +fn xor(a: bool, b: bool) -> bool { + a ^ b +} diff --git a/src/main.rs b/src/main.rs index 4a784e0..ac2726a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,9 @@ mod day20; mod day21; #[allow(dead_code)] mod day22; +#[allow(dead_code)] mod day23; +mod day24; #[allow(dead_code)] mod day3; #[allow(dead_code)] @@ -47,7 +49,7 @@ mod day8; mod day9; fn main() { - day23::day23(); + day24::day24(); } pub fn input(day: u8) -> String {