diff --git a/2022/Cargo.lock b/2022/Cargo.lock index 77c24b2..311f35a 100644 --- a/2022/Cargo.lock +++ b/2022/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "itertools", "path-absolutize", "regex", + "serde", ] [[package]] @@ -72,6 +73,24 @@ dependencies = [ "once_cell", ] +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + [[package]] name = "regex" version = "1.7.0" @@ -88,3 +107,40 @@ name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "serde" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" diff --git a/2022/Cargo.toml b/2022/Cargo.toml index ddd208f..e5abf91 100644 --- a/2022/Cargo.toml +++ b/2022/Cargo.toml @@ -8,3 +8,4 @@ anyhow = "1.0.66" itertools = "0.10.5" path-absolutize = "3.0.14" regex = "1.7.0" +serde = { version = "1.0.149", features = ["derive"] } diff --git a/2022/src/bin/day09.rs b/2022/src/bin/day09.rs new file mode 100644 index 0000000..6cc1d8d --- /dev/null +++ b/2022/src/bin/day09.rs @@ -0,0 +1,178 @@ +use std::{collections::HashSet, str::FromStr}; + +use serde::{ + de::{value, IntoDeserializer}, + Deserialize, +}; + +const INPUT: &str = include_str!("../../input/day09.txt"); + +fn main() { + println!("{}", part1(INPUT)); +} + +fn part1(input: &str) -> usize { + let steps = parse_input(input); + + let num_knots = 10; + let tail = num_knots - 1; + + let mut knots = vec![RC::new(0, 0); num_knots]; + + let mut tail_visited: HashSet = HashSet::new(); + tail_visited.insert(knots[tail]); + + // render(&knots, &tail_visited); + for step in steps { + println!("> {:?}, {}", step.d, step.n); + + for _ in 0..(step.n) { + knots[0] = knots[0].mv(step.d.delta()); + for i in 1..(knots.len()) { + knots[i] = knots[i].follow(knots[i - 1]); + } + // dbg!(head); + // dbg!(tail); + + tail_visited.insert(knots[tail]); + + // render(&knots, &tail_visited); + } + + // render(&knots, &tail_visited); + // std::thread::sleep(std::time::Duration::from_millis(500)); + } + + render(&knots, &tail_visited); + tail_visited.len() +} + +fn parse_input(input: &str) -> Vec { + aoc::split_lines(input) + .iter() + .map(|line| { + let words = aoc::split_words(line); + + let d = Direction::from_str(words[0]).unwrap(); + let n = words[1].parse::().unwrap(); + + Step { d, n } + }) + .collect() +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +struct Step { + d: Direction, + n: i64, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +struct RC { + r: i64, + c: i64, +} + +impl RC { + fn new(r: i64, c: i64) -> Self { + Self { r, c } + } + + fn origin() -> Self { + Self::new(0, 0) + } + + fn mv(&self, delta: RC) -> RC { + RC { + r: self.r + delta.r, + c: self.c + delta.c, + } + } + + fn follow(&self, head: RC) -> RC { + // dbg!(head.r); + // dbg!(head.c); + // dbg!(self.r); + // dbg!(self.c); + + let dr = head.r - self.r; + let dc = head.c - self.c; + + // dbg!(dr); + // dbg!(dc); + + // Adjacent + if dr.abs() < 2 && dc.abs() < 2 { + // dbg!("OK"); + return *self; + } + + // dbg!(sign(dr)); + // dbg!(sign(dc)); + + RC::new(self.r + sign(dr), self.c + sign(dc)) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize)] +enum Direction { + U, + D, + L, + R, +} + +impl Direction { + fn delta(&self) -> RC { + use Direction::*; + match self { + U => RC::new(1, 0), + D => RC::new(-1, 0), + L => RC::new(0, -1), + R => RC::new(0, 1), + } + } +} + +impl FromStr for Direction { + type Err = value::Error; + + fn from_str(s: &str) -> Result { + Self::deserialize(s.into_deserializer()) + } +} + +fn render(knots: &[RC], tail_visited: &HashSet) { + let rcs = knots.iter().chain(tail_visited.iter()); + let (lo, hi) = rcs.fold((RC::origin(), RC::origin()), |(lo, hi), RC { r, c }| { + ( + RC::new(lo.r.min(*r), lo.c.min(*c)), + RC::new(hi.r.max(*r), hi.c.max(*c)), + ) + }); + + let chr = move |r: i64, c: i64| -> char { + for (i, rc) in knots.iter().enumerate() { + if (r, c) == (rc.r, rc.c) { + return char::from_digit(i as u32, 10).unwrap(); + } + } + if tail_visited.contains(&RC::new(r, c)) { + '#' + } else { + '.' + } + }; + + for r in (lo.r..=hi.r).rev() { + for c in lo.c..=hi.c { + print!("{}", chr(r, c)); + } + println!(); + } + println!(); +} + +fn sign(n: i64) -> i64 { + n.cmp(&0) as i64 +}