diff --git a/Cargo.lock b/Cargo.lock index 4cfcdd1..23f1596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,7 @@ name = "advent_of_code" version = "0.11.0" dependencies = [ "chrono", + "color-eyre", "dhat", "itertools", "md5", @@ -116,6 +117,33 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -144,6 +172,16 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "gimli" version = "0.28.1" @@ -173,6 +211,12 @@ dependencies = [ "cc", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "itertools" version = "0.13.0" @@ -280,6 +324,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parking_lot" version = "0.12.1" @@ -309,6 +359,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + [[package]] name = "proc-macro2" version = "1.0.89" @@ -420,6 +476,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -459,18 +524,75 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyjson" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a" +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "wasm-bindgen" version = "0.2.95" diff --git a/Cargo.toml b/Cargo.toml index b759466..a0ca638 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ test_lib = [] # Template dependencies chrono = { version = "0.4.38", optional = true } +color-eyre = "0.6.3" dhat = { version = "0.3.3", optional = true } itertools = "0.13.0" md5 = "0.7.0" diff --git a/README.md b/README.md index ae9cb2e..ccca619 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | [Day 7](./src/bin/07.rs) | `1.5ms` | `1.3ms` | | [Day 8](./src/bin/08.rs) | `132.9ms` | `-` | | [Day 9](./src/bin/09.rs) | `77.2µs` | `969.4µs` | +| [Day 10](./src/bin/10.rs) | `164.5µs` | `287.9µs` | -**Total: 17333.30ms** +**Total: 17333.76ms** --- diff --git a/data/examples/10.txt b/data/examples/10.txt new file mode 100644 index 0000000..e9ad2b4 --- /dev/null +++ b/data/examples/10.txt @@ -0,0 +1,6 @@ +value 5 goes to bot 2 +bot 2 gives low to bot 1 and high to bot 0 +value 3 goes to bot 1 +bot 1 gives low to output 1 and high to bot 0 +bot 0 gives low to output 2 and high to output 0 +value 2 goes to bot 2 diff --git a/src/bin/10.rs b/src/bin/10.rs new file mode 100644 index 0000000..b0832e4 --- /dev/null +++ b/src/bin/10.rs @@ -0,0 +1,196 @@ +advent_of_code::solution!(10); + +use color_eyre::eyre::{eyre, Report, Result}; +use std::collections::HashMap; + +#[derive(Debug, PartialEq, Clone, Copy)] +enum GiveTo { + Bot(usize), + Output(usize), + Undef, +} + +#[derive(Debug, Clone)] +struct Bot { + high_out: GiveTo, + low_out: GiveTo, + current: [Option; 2], +} + +#[derive(Debug, Clone)] +struct State { + bots: HashMap, + outputs: HashMap, +} + +#[derive(Debug)] +enum Instruction { + GiveValue(GiveTo, u8), + BotInit(usize, GiveTo, GiveTo), +} + +impl State { + fn apply(&mut self, instr: Vec) { + instr.iter().for_each(|i| match i { + Instruction::GiveValue(GiveTo::Bot(bot), value) => { + let bot = self.bots.entry(*bot).or_insert(Bot::init()); + bot.add_value(*value); + } + Instruction::GiveValue(GiveTo::Output(out), value) => { + let out = self.outputs.entry(*out).or_default(); + if *out != 0 { + panic!("Overwriting an output ??"); + } + *out = *value; + } + Instruction::BotInit(bot, low, high) => { + let bot = self.bots.entry(*bot).or_insert(Bot::init()); + bot.set_outputs(*high, *low); + } + _ => { + println!("{:?}", i); + panic!("Unsupported instruction !"); + } + }); + } + + fn init() -> Self { + Self { + bots: HashMap::new(), + outputs: HashMap::new(), + } + } + + fn step(self) -> Option { + let mut new = self.clone(); + let new_instr: Vec = new + .bots + .iter_mut() + .flat_map(|(_, &mut ref mut bot)| bot.process()) + .collect(); + if !new_instr.is_empty() { + new.apply(new_instr); + Some(new) + } else { + None + } + } +} + +impl TryFrom<&str> for Instruction { + type Error = Report; + + fn try_from(input: &str) -> Result { + let input = input.split(" ").collect::>(); + match input.len() { + // [value] [x] [goes] [to] [bot] [y] + 6 => Ok(Instruction::GiveValue( + GiveTo::Bot(input[5].parse::()?), + input[1].parse::()?, + )), + // [bot] [x] [gives] [low] [to] [bot] [y] [and] [high] [to] [bot] [z] + 12 => { + let low: GiveTo = match input[5] { + "bot" => GiveTo::Bot(input[6].parse::()?), + "output" => GiveTo::Output(input[6].parse::()?), + _ => panic!("invalid input"), + }; + let high: GiveTo = match input[10] { + "bot" => GiveTo::Bot(input[11].parse::()?), + "output" => GiveTo::Output(input[11].parse::()?), + _ => panic!("invalid input"), + }; + Ok(Instruction::BotInit(input[1].parse::()?, low, high)) + } + _ => Err(eyre!("invalid input ?!")), + } + } +} + +impl Bot { + fn init() -> Self { + Self { + high_out: GiveTo::Undef, + low_out: GiveTo::Undef, + current: [None, None], + } + } + + fn set_outputs(&mut self, high: GiveTo, low: GiveTo) { + if self.high_out != GiveTo::Undef { + panic!("Trying to set output to already set output"); + } + self.high_out = high; + self.low_out = low; + } + + fn add_value(&mut self, value: u8) { + let value: Option = Some(value); + if self.current[0].is_none() { + self.current[0] = value; + } else if self.current[1].is_none() { + self.current[1] = value; + } else { + panic!("Giving a value to an otherwise filled bot!!"); + } + } + + fn process(&mut self) -> Vec { + let mut res: Vec = Vec::new(); + let (low, high) = match self.current { + [None, _] => return res, + [_, None] => return res, + [Some(a), Some(b)] if a < b => (a, b), + [Some(a), Some(b)] => (b, a), + }; + self.current = [None, None]; + res.push(Instruction::GiveValue(self.high_out, high)); + res.push(Instruction::GiveValue(self.low_out, low)); + res + } +} + +pub fn part_one(input: &str) -> Option { + let mut state = State::init(); + state.apply(input.lines().flat_map(Instruction::try_from).collect()); + while let Some(new_state) = state.clone().step() { + state = new_state; + for (bot_nb, bot) in state.bots.iter() { + if bot.current == [Some(61), Some(17)] || bot.current == [Some(17), Some(61)] { + return Some(*bot_nb); + } + } + } + None +} + +pub fn part_two(input: &str) -> Option { + let mut state = State::init(); + state.apply(input.lines().flat_map(Instruction::try_from).collect()); + while let Some(new_state) = state.clone().step() { + state = new_state; + } + + Some( + *state.outputs.entry(0).or_default() as u32 + * *state.outputs.entry(1).or_default() as u32 + * *state.outputs.entry(2).or_default() as u32, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, None); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(30)); + } +}