summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/day_1.rs36
-rw-r--r--src/day_2.rs170
-rw-r--r--src/day_3.rs122
-rw-r--r--src/day_4.rs74
-rw-r--r--src/day_5.rs212
-rw-r--r--src/day_6.rs80
-rw-r--r--src/day_7.rs217
-rw-r--r--src/day_8.rs326
-rw-r--r--src/lib.rs11
-rw-r--r--src/main.rs18
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)?))
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 4abd632..2f64577 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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(())
-}