diff --git a/Cargo.lock b/Cargo.lock index 1f6e5a6..f57cd6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "aoc2022" version = "0.1.0" dependencies = [ "bitvec", + "derive_more", "itertools", "nom", "num", @@ -39,6 +40,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -82,6 +89,19 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "either" version = "1.8.0" @@ -267,6 +287,24 @@ dependencies = [ "indexmap", ] +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + [[package]] name = "radium" version = "0.7.0" @@ -295,24 +333,56 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + [[package]] name = "smallvec" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 70d8474..0415259 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] bitvec = "1.0.1" +derive_more = "0.99.17" itertools = "0.10.5" nom = "7.1.1" num = "0.4.0" diff --git a/inputs/day23.txt b/inputs/day23.txt new file mode 100644 index 0000000..8926059 --- /dev/null +++ b/inputs/day23.txt @@ -0,0 +1,71 @@ +...####..#.#.#....###..#....#..#..#...##..###..##.#...######.##....#### +..#.##..#.#...###..###.##....#..##......#.###..###....#.#.#....#...#.#. +.##...#...####..####....#....#####.##.#.....#.#.#.#..#..#...####....### +.####..#.##...#.##..#.#...#..#..##.######.#######.###.##.....####.#.##. +##.##.#..######.####..##...##.#.##...###.###..#.#..####..##...###.##.#. +...###.#.###..####..###.##..#..###..#..##.###.##.##....##...##.#.#..##. +###.##...#...#.#..####..##.#..###.##..#..##.#...#...##..##..##.##.###.# +..##....###.##.#...#####.#.#.###.#.#.#.##.#...#####.#####.....###.....# +#.####.#...#.#...##..#..#.#...###.#.####.#..##..##.#.###...#.####....## +#.##...#.#####...##....##..###..#...##.###..#.#.##.##.....#####....#... +#...#..#..#.#...##..#####.#.###......##..##.##.##...###..###...#..###.. +..#.##.#..##.##.####.....#.###..#..#..#...###.###..#.#.##.##..#####..## +.##.#...#.....####......#.#..#..#.#....#.#...####...##.#.#.....#.#..... +#..#.##..###..#.....#.###..#.#..........##.......##.##.#..######...###. +.#...##.#.#...##.####..######.###.#.#.#.####.##.###..#...###.#..#..##.# +#.#.##.#.###..##.###.##.....###.#.#...#...###.##.#.#.##.##.#####.....#. +.#...###.##...#.##.#.###.###.##......##...##.#.###..#..#.#.##...####.#. +.#.#.##..#...#.##..#..##.....#....#....#.###....#########..#.#.######## +.#.##..###..##..#.......##.##..#..###.#.##.####....#..#.#...##..##.#... +#####.##.#.....#.##...#.#...#.#.####..####....#.#...#.....###...##..... +..###....#....##...#.#...###....##.###.#.#...##..##.#..#.##....#.#.#.## +########..####...#.#..##.###...##.#...#...#.######...#..#..#..#...#..## +#.#....##...##..#...#....#.##....#.###.##.#...###.#..#.#.#...###..#.#.# +#.##........#..##...#...##.##.###.#.##...##...#####.#.#.....####.#.#..# +##.##...####.##.#...#....#...###.#...#....#.#.#.####...#.#....#.####.#. +.###....#..#.#..#..####..###.#.#..#.#..#.####..#####..#.#.#..#.....##.# +.#####.#.##.###......#..##.#.....#######..##.##.#####....#..#.#..####.# +.#....###....#.##..##.#.#.#.....#####..#.#.#.##...#.#....#.##..##....#. +####....####...#.#...#..##....#.#.#.###..#.######.##..##..######...#.#. +.#..####..#####.###..#..##.#.####.##.##..#.###..#...##.#...#.#.#.#.#.## +#.###########.####.....###.#..#...###.#######...#.#...##..###....#....# +#..#.####...###..####.#.##.#.#.#.#####.###.####...#...#....########..#. +#######.#..#.#..####.##.##..##.#...##..#######..#.#..#...#..##..#.###.# +##.#.....###.#..####.##..#.#######.#.#..#.##.#..###.##..##.##.#...#.#.# +#.#######.#.##.#.###.##.##...##..#.#....#.#.#..#.#..###..##.#.#.....##. +.##.##.#...####.#####.####.####.....#..####..#..##..###...#.#.###.#...# +.####...#..#.#.#.#........#..###...#####..####..##..######.###...#####. +###....#..##.##..#####....##.#..#...##.#####..#..###.#####..###.#..##.# +..#.#...#.##..#.##.####.####..#######....##....#.#....#.#.#..##.#.#..## +.#..#.########.........###.#.....###.######..#.######...####..#..#.#.## +.#.#...###...#.#.#.#..#...##.###.#.#.##.##.....####......##.#.#....##.# +..##..##...#.#######.#.##.####.###.#..#.....#.#..#...#..####..##..#.### +.#...####.##....###..#.###...###.##...###..######.#.#.#.#.#.#.#....#.## +.###..#.###.###..##.#..##..#.#.#...#..#.#..#...##..#..#...###...#####.# +#.#..#.#.#.....##..#.##..##.#.....#..###..#.#.#.#.#.#.##..#.####..##... +..#.#.####...#.##..##.##.####..##.#.#..##.#.###.#.######..#.######.#... +###.#.#.#.####..##.######.#.#...##......###..###.#..#...#...#.#..###### +.###.#..#..######..##.####.###..##.#...#.#.#......###..##.#.#....#.#.#. +.##..#...#....#..###.##.#..#####.#.#....######..#....#..######.....#... +#.#...#..#.##...#..#..###.......####...#.####...#.#.#....#####.##...#.# +....#.####.#...#..#####.#..###..######.###.....#..#.#.#.#.#.##.#.##.... +#.......#####.#.###..##.###..#####..#.###..#.....#.###.###..###..#.##.# +##.#.#....##.#.#..#..#...###.#..#.....###...###...#..#.######.#.#...##. +#....####.##.###....#....#....#...#..####...#.#.#.###..##.#.#..##.#..## +#.....#.##....###..####..##.#.......#..#.#.#.....#....####.#...#....### +###.#..####....#.....#..####....######.....#.#....#..###.#.#..#.#..###. +#..#.#.#...##....##.##....##.....#..####.###..##.#.#.##..#.##.###...### +....###.#.#.#..###.#.##.#.#.#.#####.#####..#..##...##.##.#...####.#.### +#####.....#.#.#.#.#.#.###.#####.#...#.##..#.#.##..##..###...#...#..##.. +##.####.#.#.##..##.##.###....##.#..###.####..#######.#...##....#..##.## +##..#.####.##..#..#.#.#####.#.#.....#####..##..#.##.....##..#.#....##.# +.#..####...####.#####.##..#.##.....###.###.#.#.######..####.#...#..#.#. +##.####..##..#.....##.#.####..#..#....##...#....#####..##########.###.# +#...#.#.....#####.......###.#.#.#.###....#.#..#..#.##..#..#..#.####.### +##.###..#..##......##########....###..#######..#....#.#..###.##........ +####.........#.#...##.#.#.#..#.#.####.#####....##...#.#.##.###..#.##.## +.####.#...#..#.##.####..##..#..#...##...........##..###.####..##..#.#.. +.#####.##....##.#..####.##..##.#.###.....##.#..#..#.#.#...#.#...####... +#...####........#.##.####..##.....#####.#.#####.#####..#.##.#..##...##. +.###..###.####..###..##..##.#..##.###.#...#..#...#..#...#..#######.#.## +.#..#..##...##....##....####..#....#.##.....#.##..#.#..#...#..##.#..#.# diff --git a/src/bin/day23_1.rs b/src/bin/day23_1.rs new file mode 100644 index 0000000..d2b985b --- /dev/null +++ b/src/bin/day23_1.rs @@ -0,0 +1,8 @@ +use std::fs; + +use aoc2022::day23::process_part_1; + +fn main() { + let file = fs::read_to_string("./inputs/day23.txt").unwrap(); + println!("{}", process_part_1(&file)); +} diff --git a/src/bin/day23_2.rs b/src/bin/day23_2.rs new file mode 100644 index 0000000..4c6901e --- /dev/null +++ b/src/bin/day23_2.rs @@ -0,0 +1,8 @@ +use std::fs; + +use aoc2022::day23::process_part_2; + +fn main() { + let file = fs::read_to_string("./inputs/day23.txt").unwrap(); + println!("{}", process_part_2(&file)); +} diff --git a/src/day23.rs b/src/day23.rs index 64174de..21a6d68 100644 --- a/src/day23.rs +++ b/src/day23.rs @@ -1,24 +1,180 @@ -pub fn process_part_1(_input: &str) -> u32 { - 0 +use std::collections::BTreeSet; +use std::fmt::{Display, Formatter}; + +use derive_more::{Add, Sub}; +use itertools::Itertools; +use itertools::MinMaxResult::MinMax; +use nom::character::complete::{newline, one_of}; +use nom::multi::{many1, separated_list1}; +use nom::IResult; + +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Add, Sub)] +struct Vec2 { + x: i64, + y: i64, +} +impl Display for Vec2 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } } -pub fn process_part_2(_input: &str) -> u32 { - 0 +impl From<(usize, usize)> for Vec2 { + fn from(value: (usize, usize)) -> Self { + Self { + x: value.0 as i64, + y: value.1 as i64, + } + } +} + +// didn't wanna write them myself +macro_rules! vec2 { + ($x:expr,$y:expr) => { + Vec2 { x: $x, y: $y } + }; +} + +const DIRECTIONS: [[Vec2; 3]; 4] = [ + [vec2!(-1, -1), vec2!(0, -1), vec2!(1, -1)], // North: NW, N, NE + [vec2!(-1, 1), vec2!(0, 1), vec2!(1, 1)], // South: SW, S, SE + [vec2!(-1, -1), vec2!(-1, 0), vec2!(-1, 1)], // West: NW, W, SW + [vec2!(1, -1), vec2!(1, 0), vec2!(1, 1)], // East: NE, E, SE +]; + +pub fn process_part_1(input: &str) -> usize { + let mut elves = parse_input(input).unwrap().1; + for i in 0..10 { + elves.step(i); + } + elves.bounding_box_area() - elves.0.len() +} + +pub fn process_part_2(input: &str) -> usize { + let mut elves = parse_input(input).unwrap().1; + (0..) + .take_while(|i| elves.step(*i)) + .last() + .map(|x| x + 2) // we start counting at 0 while aoc expects 1, and take_while 'noms' the one we need to keep away + .unwrap_or(0) +} + +fn parse_input(input: &str) -> IResult<&str, Elves> { + let (input, items) = separated_list1(newline, many1(one_of(".#")))(input)?; + let result = items + .iter() + .enumerate() + .flat_map(|(y, row)| { + row.iter() + .enumerate() + .flat_map(move |(x, &elem)| match elem { + '#' => Some(Vec2::from((x, y))), + _ => None, + }) + }) + .collect(); + Ok((input, Elves(result))) +} + +#[derive(Debug)] +struct Elves(BTreeSet); + +impl Elves { + fn step(&mut self, dir_counter: usize) -> bool { + let mut desired_moves = Vec::new(); + let mut has_moved = false; + for elf in self.0.iter() { + if !self.has_elf_near(*elf) { + continue; + } + + for i in 0..4 { + let offsets = DIRECTIONS[(dir_counter + i) % 4]; + if !self.has_elf(*elf, &offsets) { + let next = *elf + offsets[1]; + desired_moves.push((next, *elf)); + break; + } + } + } + let moves = desired_moves + .into_iter() + .sorted_unstable_by(|a, b| a.0.cmp(&b.0)) + .dedup_by_with_count(|a, b| a.0 == b.0) + .filter(|(count, _)| *count == 1) + .map(|(_, m)| m); + for (next, elf) in moves { + self.0.remove(&elf); + self.0.insert(next); + has_moved = true; + } + has_moved + } + + fn has_elf_near(&self, pos: Vec2) -> bool { + DIRECTIONS + .iter() + .flatten() + .any(|d| self.0.contains(&(pos + *d))) + } + + fn has_elf(&self, pos: Vec2, offsets: &[Vec2]) -> bool { + offsets.iter().any(|d| self.0.contains(&(pos + *d))) + } + + fn bounding_box(&self) -> (Vec2, Vec2) { + let MinMax(min_x, max_x) = self.0.iter().minmax_by_key(|p| p.x) else { + panic!("no min/max x?"); + }; + let MinMax(min_y, max_y) = self.0.iter().minmax_by_key(|p| p.y) else { + panic!("no min/max y?"); + }; + (vec2!(min_x.x, min_y.y), vec2!(max_x.x, max_y.y)) + } + + fn bounding_box_area(&self) -> usize { + let (min, max) = self.bounding_box(); + ((max.x - min.x + 1) * (max.y - min.y + 1)) as usize + } +} + +impl Display for Elves { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let (min, max) = self.bounding_box(); + for y in min.y..=max.y { + for x in min.x..=max.x { + let elf = if self.0.contains(&vec2!(x, y)) { + '#' + } else { + '.' + }; + write!(f, "{elf}")?; + } + writeln!(f)?; + } + Ok(()) + } } #[cfg(test)] mod tests { use super::*; - const INPUT: &str = ""; + const INPUT: &str = "....#.. +..###.# +#...#.# +.#...## +#.###.. +##.#.## +.#..#.."; #[test] fn day1() { - assert_eq!(process_part_1(INPUT), 0); + assert_eq!(process_part_1(INPUT), 110); } #[test] fn day2() { - assert_eq!(process_part_2(INPUT), 0); + assert_eq!(process_part_2(INPUT), 20); } }