diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/day_1.rs | 36 | ||||
-rw-r--r-- | src/day_2.rs | 170 | ||||
-rw-r--r-- | src/day_3.rs | 122 | ||||
-rw-r--r-- | src/day_4.rs | 74 | ||||
-rw-r--r-- | src/day_5.rs | 212 | ||||
-rw-r--r-- | src/day_6.rs | 80 | ||||
-rw-r--r-- | src/day_7.rs | 217 | ||||
-rw-r--r-- | src/day_8.rs | 326 | ||||
-rw-r--r-- | src/lib.rs | 11 | ||||
-rw-r--r-- | src/main.rs | 18 |
10 files changed, 0 insertions, 1266 deletions
diff --git a/src/day_1.rs b/src/day_1.rs deleted file mode 100644 index 8fcc6ce..0000000 --- a/src/day_1.rs +++ /dev/null @@ -1,36 +0,0 @@ -use anyhow::{Context, Result}; - -use crate::{Problem, Solution}; - -pub struct Day1; - -impl Problem for Day1 { - const DAY: u8 = 1; - - const INPUT: &'static str = include_str!("../input/day_1.txt"); -} - -impl Solution for Day1 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - input - .split("\n\n") - .map(|e| e.split('\n').flat_map(|l| l.parse::<usize>()).sum()) - .max() - .context("Failed to find max") - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - let mut vec = input - .split("\n\n") - .map(|e| e.split('\n').flat_map(|l| l.parse::<usize>()).sum()) - .collect::<Vec<usize>>(); - - vec.sort_unstable(); - - Ok(vec.iter().rev().take(3).sum()) - } -} diff --git a/src/day_2.rs b/src/day_2.rs deleted file mode 100644 index f9e207b..0000000 --- a/src/day_2.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::str::FromStr; - -use anyhow::{anyhow, Result}; - -use crate::{Problem, Solution}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct Game { - shapes: (Shape, Shape), - outcome: Outcome, - score: usize, -} - -impl Game { - fn new(a: Shape, b: Shape, outcome: Outcome) -> Self { - Self { - shapes: (a, b), - outcome, - score: b as usize + outcome as usize, - } - } -} - -impl From<(Shape, Shape)> for Game { - fn from((a, b): (Shape, Shape)) -> Self { - Self::new(a, b, (a, b).into()) - } -} - -impl From<(Shape, Outcome)> for Game { - fn from((a, outcome): (Shape, Outcome)) -> Self { - Self::new(a, (a, outcome).into(), outcome) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -enum Outcome { - Loss = 0, - Draw = 3, - Win = 6, -} - -impl From<(Shape, Shape)> for Outcome { - fn from((a, b): (Shape, Shape)) -> Self { - match (a, b) { - (Shape::Rock, Shape::Rock) => Outcome::Draw, - (Shape::Rock, Shape::Paper) => Outcome::Win, - (Shape::Rock, Shape::Scissors) => Outcome::Loss, - (Shape::Paper, Shape::Rock) => Outcome::Loss, - (Shape::Paper, Shape::Paper) => Outcome::Draw, - (Shape::Paper, Shape::Scissors) => Outcome::Win, - (Shape::Scissors, Shape::Rock) => Outcome::Win, - (Shape::Scissors, Shape::Paper) => Outcome::Loss, - (Shape::Scissors, Shape::Scissors) => Outcome::Draw, - } - } -} - -impl FromStr for Outcome { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "X" => Ok(Outcome::Loss), - "Y" => Ok(Outcome::Draw), - "Z" => Ok(Outcome::Win), - s => Err(anyhow!("Unknown symbol: {}", s)), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Shape { - Rock = 1, - Paper = 2, - Scissors = 3, -} - -impl From<(Shape, Outcome)> for Shape { - fn from((shape, outcome): (Shape, Outcome)) -> Self { - match (shape, outcome) { - (Shape::Rock, Outcome::Draw) => Shape::Rock, - (Shape::Rock, Outcome::Win) => Shape::Paper, - (Shape::Rock, Outcome::Loss) => Shape::Scissors, - (Shape::Paper, Outcome::Loss) => Shape::Rock, - (Shape::Paper, Outcome::Draw) => Shape::Paper, - (Shape::Paper, Outcome::Win) => Shape::Scissors, - (Shape::Scissors, Outcome::Win) => Shape::Rock, - (Shape::Scissors, Outcome::Loss) => Shape::Paper, - (Shape::Scissors, Outcome::Draw) => Shape::Scissors, - } - } -} - -impl FromStr for Shape { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "A" | "X" => Ok(Shape::Rock), - "B" | "Y" => Ok(Shape::Paper), - "C" | "Z" => Ok(Shape::Scissors), - s => Err(anyhow!("Unknown symbol: {}", s)), - } - } -} - -pub struct Day2; - -impl Problem for Day2 { - const DAY: u8 = 3; - - const INPUT: &'static str = include_str!("../input/day_2.txt"); -} - -impl Solution for Day2 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - input.lines().try_fold(0, |acc, l| { - let (a, b) = l - .split_once(' ') - .ok_or_else(|| anyhow!("Missing deliminator"))?; - - let a: Shape = a.parse()?; - let b: Shape = b.parse()?; - let game: Game = (a, b).into(); - - Ok(acc + game.score) - }) - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - input.lines().try_fold(0, |acc, l| { - let (a, outcome) = l - .split_once(' ') - .ok_or_else(|| anyhow!("Missing deliminator"))?; - - let a: Shape = a.parse()?; - let outcome: Outcome = outcome.parse()?; - let game: Game = (a, outcome).into(); - - Ok(acc + game.score) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const INPUT: &str = indoc::indoc! {" - A Y - B X - C Z - "}; - - #[test] - fn test_part_1_example() -> Result<()> { - Ok(assert_eq!(15, Day2::part_1(INPUT)?)) - } - - #[test] - fn test_part_2_example() -> Result<()> { - Ok(assert_eq!(12, Day2::part_2(INPUT)?)) - } -} diff --git a/src/day_3.rs b/src/day_3.rs deleted file mode 100644 index a549e52..0000000 --- a/src/day_3.rs +++ /dev/null @@ -1,122 +0,0 @@ -use std::{ops::Deref, str::FromStr}; - -use anyhow::{anyhow, Context, Result}; - -use crate::{Problem, Solution}; - -trait Priority { - fn priority(self) -> Result<u8>; -} - -impl Priority for char { - fn priority(self) -> Result<u8> { - match self as u8 { - c @ 65..=90 => Ok(c - 65 + 27), - c @ 97..=122 => Ok(c - 97 + 1), - c => Err(anyhow!("failed to get priority of {}", c as char)), - } - } -} - -#[derive(Debug, Clone)] -struct Backpack(Vec<char>); - -impl Backpack { - fn get_local_union(self) -> Result<char> { - let (left, right) = self.split_at(self.len() / 2); - left.iter() - .cloned() - .find(|&item| right.contains(&item)) - .context(format!("Failed to find union in {:?}", &self)) - } - - fn get_group_union(group: &mut Vec<Backpack>) -> Result<char> { - group - .split_first_mut() - .and_then(|(first, others)| { - first - .iter() - .cloned() - .find(|item| others.iter_mut().all(|b| b.contains(item))) - }) - .context(format!("Failed to find union in {:?}", group)) - } -} -impl Deref for Backpack { - type Target = Vec<char>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::str::FromStr for Backpack { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self> { - Ok(Backpack(s.as_bytes().iter().map(|c| *c as char).collect())) - } -} - -pub struct Day3; - -impl Problem for Day3 { - const DAY: u8 = 3; - - const INPUT: &'static str = include_str!("../input/day_3.txt"); -} - -impl Solution for Day3 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - input - .lines() - .map(Backpack::from_str) - .try_collect::<Vec<_>>()? - .into_iter() - .map(|b| b.get_local_union()) - .try_collect::<Vec<_>>()? - .into_iter() - .try_fold(0, |sum, c| c.priority().map(|p| sum + p as usize)) - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - let lines: Vec<&str> = input.lines().collect(); - lines.as_slice().chunks(3).try_fold(0, |acc, g| { - let mut group = g - .iter() - .cloned() - .map(Backpack::from_str) - .try_collect::<Vec<_>>()?; - - Ok(Backpack::get_group_union(&mut group)?.priority()? as usize + acc) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const INPUT: &str = indoc::indoc! {r#" - vJrwpWtwJgWrhcsFMMfFFhFp - jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL - PmmdzqPrVvPwwTWBwg - wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn - ttgJtRGJQctTZtZT - CrZsJsPPZsGzwwsLwLmpwMDw - "#}; - - #[test] - fn test_part_1_example() -> Result<()> { - Ok(assert_eq!(157, Day3::part_1(INPUT)?)) - } - - #[test] - fn test_part_2_example() -> Result<()> { - Ok(assert_eq!(70, Day3::part_2(INPUT)?)) - } -} diff --git a/src/day_4.rs b/src/day_4.rs deleted file mode 100644 index 690ccd1..0000000 --- a/src/day_4.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::ops::Range; - -use anyhow::Result; - -use crate::{Problem, Solution}; - -pub fn parse_range(section: &str) -> Result<Range<usize>> { - let (start, end) = section.split_once('-').unwrap(); - let start = start.parse::<usize>()?; - let end = end.parse::<usize>()?; - Ok(Range { start, end }) -} - -fn parse_pair(input: &str) -> Result<(Range<usize>, Range<usize>)> { - let (section_a, section_b) = input.split_once(',').unwrap(); - - Ok((parse_range(section_a)?, parse_range(section_b)?)) -} - -fn range_is_subset((a, b): &(Range<usize>, Range<usize>)) -> bool { - (a.start <= b.start && a.end >= b.end) || (b.start <= a.start && b.end >= a.end) -} - -fn range_is_union((a, b): &(Range<usize>, Range<usize>)) -> bool { - (a.start <= b.start && a.end >= b.start) || (b.start <= a.start && b.end >= a.start) -} - -pub struct Day4; - -impl Problem for Day4 { - const DAY: u8 = 4; - - const INPUT: &'static str = include_str!("../input/day_4.txt"); -} - -impl Solution for Day4 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - let ranges: Vec<_> = input.lines().map(parse_pair).collect::<Result<_>>()?; - Ok(ranges.into_iter().filter(range_is_subset).count()) - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - let ranges: Vec<_> = input.lines().map(parse_pair).collect::<Result<_>>()?; - Ok(ranges.into_iter().filter(range_is_union).count()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const TEST_INPUT: &str = r#"2-4,6-8 -2-3,4-5 -5-7,7-9 -2-8,3-7 -6-6,4-6 -2-6,4-8"#; - - #[test] - fn test_part_1_example() -> Result<()> { - assert_eq!(2, Day4::part_1(TEST_INPUT)?); - Ok(()) - } - - #[test] - fn test_part_2_example() -> Result<()> { - assert_eq!(4, Day4::part_2(TEST_INPUT)?); - Ok(()) - } -} diff --git a/src/day_5.rs b/src/day_5.rs deleted file mode 100644 index 006a59a..0000000 --- a/src/day_5.rs +++ /dev/null @@ -1,212 +0,0 @@ -use std::{ - fmt::Display, - ops::{Deref, DerefMut}, - str::FromStr, -}; - -use anyhow::{Context, Result}; - -use crate::{Problem, Solution}; - -#[derive(Debug)] -struct Procedure(Vec<Step>); - -impl Procedure { - fn run(self, mut stacks: Stacks, in_order: bool) -> Result<Stacks> { - for step in self.0 { - let mut move_stack = Vec::new(); - for _ in 0..step.count { - let cargo = stacks[step.from - 1] - .pop() - .ok_or_else(|| anyhow::anyhow!("ran out of cargo"))?; - move_stack.push(cargo) - } - - if in_order { - move_stack.reverse() - } - - stacks[step.to - 1].append(&mut move_stack); - } - Ok(stacks) - } -} - -impl FromStr for Procedure { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self> { - let mut procedure = Procedure(Vec::new()); - for (linenr, step) in s.lines().enumerate() { - procedure.push( - step.parse() - .context(format!("Error in procedure step {}: '{}'", linenr, step))?, - ) - } - Ok(procedure) - } -} - -impl Deref for Procedure { - type Target = Vec<Step>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Procedure { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -#[derive(Debug)] -struct Step { - count: usize, - from: usize, - to: usize, -} - -impl FromStr for Step { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self> { - let mut items = s.split_whitespace(); - Ok(Self { - count: items.next_chunk::<2>().unwrap().last().unwrap().parse()?, - from: items.next_chunk::<2>().unwrap().last().unwrap().parse()?, - to: items.next_chunk::<2>().unwrap().last().unwrap().parse()?, - }) - } -} - -#[derive(Debug)] -struct Stacks(Vec<Vec<char>>); - -impl Stacks { - fn top(mut self) -> String { - self.iter_mut().fold( - "".to_owned(), - |mut acc: String, s: &mut std::vec::Vec<char>| { - if let Some(c) = s.pop() { - acc.push(c) - } - acc - }, - ) - } -} - -impl Deref for Stacks { - type Target = Vec<Vec<char>>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Stacks { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl FromStr for Stacks { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self> { - let mut stacks = Self(Vec::new()); - for line in s.lines().rev().skip(1) { - let mut chars = line.chars().skip(1).enumerate(); - loop { - let (stack, cargo) = match chars.next() { - Some((_, '[' | ']' | ' ')) => continue, - Some((index, c)) => (index / 4, c), - None => break, - }; - if stacks.len() < stack + 1 { - stacks.push(Vec::new()) - } - stacks[stack].push(cargo) - } - } - Ok(stacks) - } -} - -impl Display for Stacks { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Some(max_len) = self.0.iter().map(|v| v.len()).max() else { - return Ok(()) - }; - - for i in (0..max_len).rev() { - let mut cargos = Vec::new(); - for stack in &self.0 { - let cargo = match stack.get(i) { - Some(c) => format!("[{}]", c), - None => " ".to_owned(), - }; - cargos.push(cargo); - } - writeln!(f, "{}", cargos.join(" "))? - } - Ok(()) - } -} - -pub struct Day5; - -impl Problem for Day5 { - const DAY: u8 = 5; - - const INPUT: &'static str = include_str!("../input/day_5.txt"); -} - -impl Solution for Day5 { - type Answer1 = String; - - type Answer2 = String; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - let (stacks, procedure) = input.split_once("\n\n").unwrap(); - let stacks = Stacks::from_str(stacks)?; - let procedure = Procedure::from_str(procedure)?; - Ok(procedure.run(stacks, false)?.top()) - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - let (stacks, procedure) = input.split_once("\n\n").unwrap(); - let stacks = Stacks::from_str(stacks)?; - let procedure = Procedure::from_str(procedure)?; - Ok(procedure.run(stacks, true)?.top()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const TEST_INPUT: &str = indoc::indoc! {r#" - [D] - [N] [C] - [Z] [M] [P] - 1 2 3 - - move 1 from 2 to 1 - move 3 from 1 to 3 - move 2 from 2 to 1 - move 1 from 1 to 2 - "#}; - - #[test] - fn test_part_1_example() -> Result<()> { - Ok(assert_eq!("CMZ", Day5::part_1(TEST_INPUT)?)) - } - - #[test] - fn test_part_2_example() -> Result<()> { - Ok(assert_eq!("MCD", Day5::part_2(TEST_INPUT)?)) - } -} diff --git a/src/day_6.rs b/src/day_6.rs deleted file mode 100644 index 5983400..0000000 --- a/src/day_6.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::collections::HashSet; - -use anyhow::Result; - -use crate::{Problem, Solution}; - -fn get_window_pos(input: &str, win_size: usize) -> Option<usize> { - input - .as_bytes() - .windows(win_size) - .position(|set| { - let mut h = HashSet::new(); - for &c in set { - if !h.insert(c) { - return false; - } - } - true - }) - .map(|i| i + win_size) -} - -pub struct Day6; - -impl Problem for Day6 { - const DAY: u8 = 6; - - const INPUT: &'static str = include_str!("../input/day_6.txt"); -} - -impl Solution for Day6 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - get_window_pos(input, 4).ok_or_else(|| anyhow::anyhow!("Failed to find item")) - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - get_window_pos(input, 14).ok_or_else(|| anyhow::anyhow!("Failed to find item")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_part_1_example() -> Result<()> { - let tests = vec![ - ("mjqjpqmgbljsphdztnvjfqwrcgsmlb", 7), - ("bvwbjplbgvbhsrlpgdmjqwftvncz", 5), - ("nppdvjthqldpwncqszvftbrmjlhg", 6), - ("nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg", 10), - ("zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw", 11), - ]; - for test in tests { - assert_eq!(test.1, Day6::part_1(test.0)?); - } - - Ok(()) - } - - #[test] - fn test_part_2_example() -> Result<()> { - let tests = vec![ - ("mjqjpqmgbljsphdztnvjfqwrcgsmlb", 19), - ("bvwbjplbgvbhsrlpgdmjqwftvncz", 23), - ("nppdvjthqldpwncqszvftbrmjlhg", 23), - ("nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg", 29), - ("zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw", 26), - ]; - for test in tests { - assert_eq!(test.1, Day6::part_2(test.0)?); - } - - Ok(()) - } -} diff --git a/src/day_7.rs b/src/day_7.rs deleted file mode 100644 index 70e5448..0000000 --- a/src/day_7.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::{collections::HashMap, fmt::Display, ops::Deref, str::FromStr}; - -use anyhow::{anyhow, Context, Result}; - -use crate::{Problem, Solution}; - -enum Cmd { - Root, - Parent, - Child(String), - LS(Vec<Entry>), -} - -impl FromStr for Cmd { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - let (cmd, arg) = s.trim().split_at(2); - let cmd_arg = (cmd.trim(), arg.trim()); - match cmd_arg { - ("cd", "/") => Ok(Self::Root), - ("cd", "..") => Ok(Self::Parent), - ("cd", child) => Ok(Self::Child(child.to_owned())), - ("ls", arg) => { - let entries = arg - .trim() - .lines() - .map(Entry::from_str) - .collect::<Result<Vec<_>>>()?; - Ok(Cmd::LS(entries)) - } - _ => Err(anyhow!("Failed to parse Command from str: {}", s)), - } - } -} - -enum Entry { - Dir(String), - File((String, usize)), -} - -impl FromStr for Entry { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - let (entry_type, name) = s - .split_once(' ') - .context(format!("Failed to parse Command from str: {}", s))?; - match entry_type { - "dir" => Ok(Self::Dir(name.to_owned())), - t => Ok(Self::File((name.to_owned(), t.parse()?))), - } - } -} - -#[derive(Default, Debug)] -struct FileSystem { - entries: HashMap<Vec<String>, usize>, - cwd: Vec<String>, -} - -impl FileSystem { - fn add(&mut self, size: usize) { - self.entries - .entry(self.cwd.to_owned()) - .and_modify(|n| *n += size) - .or_insert(size); - } - - fn pop_and_add(&mut self) -> Option<String> { - let size = *self.entries.get(&self.cwd).unwrap_or(&0); - let value = self.cwd.pop(); - self.add(size); - value - } - - fn exec_cmd(&mut self, cmd: Cmd) { - match cmd { - Cmd::Root => self.cwd = vec![], - Cmd::Parent => { - self.pop_and_add(); - } - Cmd::Child(d) => self.cwd.push(d), - Cmd::LS(entries) => { - for entry in entries.into_iter() { - match entry { - Entry::File((_, size)) => self.add(size), - Entry::Dir(_) => {} - } - } - } - } - } -} - -impl Deref for FileSystem { - type Target = HashMap<Vec<String>, usize>; - - fn deref(&self) -> &Self::Target { - &self.entries - } -} - -impl FromStr for FileSystem { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - s.split('$') - .filter_map(|l| match l.trim() { - _ if l.is_empty() => None, - l => Some(l), - }) - .flat_map(Cmd::from_str) - .collect::<Vec<Cmd>>() - .try_into() - } -} - -impl TryFrom<Vec<Cmd>> for FileSystem { - type Error = anyhow::Error; - - fn try_from(value: Vec<Cmd>) -> Result<Self, Self::Error> { - let mut fs = Self::default(); - for cmd in value { - fs.exec_cmd(cmd) - } - - while !fs.cwd.is_empty() { - fs.exec_cmd(Cmd::Parent) - } - - Ok(fs) - } -} - -impl Display for FileSystem { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "path\tsize")?; - for (path, size) in &self.entries { - writeln!(f, "/{}\t{}", path.join("/"), size)?; - } - Ok(()) - } -} - -pub struct Day7; - -impl Problem for Day7 { - const DAY: u8 = 7; - - const INPUT: &'static str = include_str!("../input/day_7.txt"); -} - -impl Solution for Day7 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1> { - let fs: FileSystem = input.parse()?; - Ok(fs.values().filter(|&s| *s < 100_000).sum()) - } - - fn part_2(input: &str) -> Result<Self::Answer2> { - let fs: FileSystem = input.parse()?; - let used = *fs.get(&Vec::new()).context("Failed to get root size")?; - - fs.values() - .filter_map(|&dir_size| match dir_size { - k if used - k < 40_000_000 => Some(k), - _ => None, - }) - .min() - .context("No directory found") - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const TEST_INPUT: &str = indoc::indoc! {r#" - $ cd / - $ ls - dir a - 14848514 b.txt - 8504156 c.dat - dir d - $ cd a - $ ls - dir e - 29116 f - 2557 g - 62596 h.lst - $ cd e - $ ls - 584 i - $ cd .. - $ cd .. - $ cd d - $ ls - 4060174 j - 8033020 d.log - 5626152 d.ext - 7214296 k - "#}; - - #[test] - fn test_part_1_example() -> Result<()> { - Ok(assert_eq!(95437, Day7::part_1(TEST_INPUT)?)) - } - - #[test] - fn test_part_2_example() -> Result<()> { - Ok(assert_eq!(24933642, Day7::part_2(TEST_INPUT)?)) - } -} diff --git a/src/day_8.rs b/src/day_8.rs deleted file mode 100644 index 145bc22..0000000 --- a/src/day_8.rs +++ /dev/null @@ -1,326 +0,0 @@ -use std::{collections::HashSet, fmt::Display}; - -use anyhow::Result; - -use crate::{Problem, Solution}; - -type TreeHeight = i8; - -struct Visibility { - capacity_x: usize, - capacity_y: usize, - trees_seen: HashSet<(usize, usize)>, - max_north: Vec<TreeHeight>, - max_east: Vec<TreeHeight>, - max_south: Vec<TreeHeight>, - max_west: Vec<TreeHeight>, -} - -impl Visibility { - fn with_capacity(capacity_x: usize, capacity_y: usize) -> Self { - Self { - capacity_x, - capacity_y, - trees_seen: HashSet::new(), - max_north: vec![-1; capacity_x], - max_east: vec![-1; capacity_y], - max_south: vec![-1; capacity_x], - max_west: vec![-1; capacity_y], - } - } - - fn check_trees(&mut self, tree_grid: Vec<Vec<TreeHeight>>) { - self.check_trees_nw(&tree_grid); - self.check_trees_se(&tree_grid); - } - - fn check_trees_nw(&mut self, tree_grid: &[Vec<TreeHeight>]) { - for (y, trees) in tree_grid.iter().enumerate() { - for (x, tree) in trees.iter().enumerate() { - self.check_tree_nw(x, y, *tree) - } - } - } - - fn check_trees_se(&mut self, tree_grid: &[Vec<TreeHeight>]) { - for (y, trees) in tree_grid.iter().enumerate().rev() { - for (x, tree) in trees.iter().enumerate().rev() { - self.check_tree_se(x, y, *tree) - } - } - } - - fn check_tree_nw(&mut self, x: usize, y: usize, tree: TreeHeight) { - if tree > self.max_north[x] { - self.trees_seen.insert((x, y)); - self.max_north[x] = tree; - } - - if tree > self.max_west[y] { - self.trees_seen.insert((x, y)); - self.max_west[y] = tree; - } - } - - fn check_tree_se(&mut self, x: usize, y: usize, tree: TreeHeight) { - if tree > self.max_south[x] { - self.trees_seen.insert((x, y)); - self.max_south[x] = tree; - } - - if tree > self.max_east[y] { - self.trees_seen.insert((x, y)); - self.max_east[y] = tree; - } - } -} - -impl Display for Visibility { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for y in 0..self.capacity_y { - let mut l = String::new(); - for x in 0..self.capacity_x { - l.push(if self.trees_seen.contains(&(x, y)) { - 'X' - } else { - ' ' - }) - } - writeln!(f, "{}", l)?; - } - Ok(()) - } -} - -#[derive(Debug, Default)] -struct ScenicScore { - tree: Tree, - north: Vec<Tree>, - east: Vec<Tree>, - south: Vec<Tree>, - west: Vec<Tree>, -} - -impl ScenicScore { - fn new( - tree: Tree, - north: Vec<Tree>, - east: Vec<Tree>, - south: Vec<Tree>, - west: Vec<Tree>, - ) -> Self { - Self { - tree, - north, - east, - south, - west, - } - } - fn score(&self) -> usize { - self.north.len() * self.east.len() * self.south.len() * self.west.len() - } -} - -impl Display for ScenicScore { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for t in &self.north { - for _ in 0..self.tree.x { - write!(f, "x")?; - } - write!(f, "{t:>width$}", width = self.tree.x)?; - for _ in self.tree.x..self.east.len() { - write!(f, "x")?; - } - writeln!(f)?; - } - - let line = self - .west - .iter() - .chain([&self.tree]) - .chain(self.east.iter()) - .map(|t| t.to_string()) - .collect::<Vec<_>>() - .join(""); - - let offset = self.tree.x - self.west.len(); - writeln!(f, "{:offset$}", line)?; - - for t in &self.south { - for _ in 0..self.tree.x { - write!(f, "x")?; - } - writeln!(f, "{t:>width$}", width = self.tree.x)?; - for _ in self.tree.x..self.east.len() { - write!(f, "x")?; - } - } - - Ok(()) - } -} - -impl PartialEq for ScenicScore { - fn eq(&self, other: &Self) -> bool { - self.score() == other.score() - } -} - -impl Eq for ScenicScore {} - -impl PartialOrd for ScenicScore { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.score().partial_cmp(&other.score()) - } -} - -impl Ord for ScenicScore { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.score().cmp(&other.score()) - } -} - -#[derive(Debug, Default, Clone, Copy)] -struct Tree { - x: usize, - y: usize, - height: TreeHeight, -} - -impl Tree { - fn new(x: usize, y: usize, height: TreeHeight) -> Self { - Self { x, y, height } - } - - fn scenic_score(&self, tree_grid: &[Vec<Tree>]) -> ScenicScore { - let mut north: Vec<Tree> = Vec::new(); - for t in tree_grid.iter().map(|v| v[self.x]).take(self.y).rev() { - north.push(t); - if t >= *self { - break; - } - } - north.reverse(); - - let mut east: Vec<Tree> = Vec::new(); - for t in tree_grid[self.y].iter().skip(self.x + 1) { - east.push(*t); - if t >= self { - break; - } - } - - let mut south: Vec<Tree> = Vec::new(); - for t in tree_grid.iter().map(|v| v[self.x]).skip(self.y + 1) { - south.push(t); - if t >= *self { - break; - } - } - - let mut west: Vec<Tree> = Vec::new(); - for t in tree_grid[self.y].iter().take(self.x).rev() { - west.push(*t); - if t >= self { - break; - } - } - west.reverse(); - - ScenicScore::new(*self, north, east, south, west) - } -} - -impl PartialEq for Tree { - fn eq(&self, other: &Self) -> bool { - self.height == other.height - } -} - -impl PartialOrd for Tree { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.height.partial_cmp(&other.height) - } -} - -impl Display for Tree { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.height) - } -} - -pub struct Day8; - -impl Problem for Day8 { - const DAY: u8 = 8; - - const INPUT: &'static str = include_str!("../input/day_8.txt"); -} - -impl Solution for Day8 { - type Answer1 = usize; - - type Answer2 = usize; - - fn part_1(input: &str) -> Result<Self::Answer1, anyhow::Error> { - let tree_grid: Vec<Vec<TreeHeight>> = input - .lines() - .map(|l| { - l.chars() - .filter_map(|c| c.to_digit(10).map(|d| d as TreeHeight)) - .collect() - }) - .collect(); - - let mut visibility = Visibility::with_capacity(tree_grid[0].len(), tree_grid.len()); - visibility.check_trees(tree_grid); - Ok(visibility.trees_seen.len()) - } - - fn part_2(input: &str) -> Result<Self::Answer2, anyhow::Error> { - let tree_grid: Vec<Vec<Tree>> = input - .lines() - .enumerate() - .map(|(y, l)| { - l.chars() - .enumerate() - .filter_map(|(x, c)| c.to_digit(10).map(|d| Tree::new(x, y, d as TreeHeight))) - .collect() - }) - .collect(); - - let trees = tree_grid.iter().flatten(); - - let max_score = trees - .map(|t| t.scenic_score(&tree_grid)) - .max() - .ok_or_else(|| anyhow::anyhow!("Failed to find max"))?; - - Ok(max_score.score()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const TEST_INPUT: &str = indoc::indoc! {r#" - 30373 - 25512 - 65332 - 33549 - 35390 - "#}; - - #[test] - fn test_part_1_example() -> Result<()> { - Ok(assert_eq!(21, Day8::part_1(TEST_INPUT)?)) - } - - #[test] - fn test_part_2_example() -> Result<()> { - println!("{TEST_INPUT}"); - Ok(assert_eq!(8, Day8::part_2(TEST_INPUT)?)) - } -} @@ -1,5 +1,3 @@ -#![feature(iterator_try_collect)] -#![feature(iter_next_chunk)] #![feature(associated_type_defaults)] pub trait Solution: Problem { @@ -25,12 +23,3 @@ pub trait Problem { const INPUT: &'static str; } - -pub mod day_1; -pub mod day_2; -pub mod day_3; -pub mod day_4; -pub mod day_5; -pub mod day_6; -pub mod day_7; -pub mod day_8; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 352aca6..0000000 --- a/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -use advent_of_code_2022::{ - day_1::Day1, day_2::Day2, day_3::Day3, day_4::Day4, day_5::Day5, day_6::Day6, day_7::Day7, - day_8::Day8, Solution, -}; -use anyhow::Result; - -fn main() -> Result<()> { - Day1::solve()?; - Day2::solve()?; - Day3::solve()?; - Day4::solve()?; - Day5::solve()?; - Day6::solve()?; - Day7::solve()?; - Day8::solve()?; - - Ok(()) -} |