From 889c5ff8d2c259865ddd60cd5cb0610b99e201a6 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Tue, 6 Dec 2022 15:01:49 -0600 Subject: feat: impl day 5 --- src/day_5.rs | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 src/day_5.rs (limited to 'src/day_5.rs') diff --git a/src/day_5.rs b/src/day_5.rs new file mode 100644 index 0000000..10eb2bc --- /dev/null +++ b/src/day_5.rs @@ -0,0 +1,207 @@ +use std::{ + fmt::Display, + ops::{Deref, DerefMut}, + str::FromStr, +}; + +use anyhow::{Context, Result}; + +#[derive(Debug)] +struct Procedure(Vec); + +impl Procedure { + fn run(self, mut stacks: Stacks, in_order: bool) -> Result { + 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 { + 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; + + 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 { + 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>); + +impl Stacks { + fn top(mut self) -> String { + self.iter_mut().fold( + "".to_owned(), + |mut acc: String, s: &mut std::vec::Vec| { + if let Some(c) = s.pop() { + acc.push(c) + } + acc + }, + ) + } +} + +impl Deref for Stacks { + type Target = Vec>; + + 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 { + 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(()) + } +} + +const DAY: u8 = 5; +const INPUT: &str = include_str!("../input/day_5.txt"); + +pub fn solve() -> Result<()> { + println!("day {}", DAY); + println!("part 1: {}", part_1(INPUT)?); + println!("part 2: {}", part_2(INPUT)?); + + Ok(()) +} + +pub fn part_1(input: &str) -> Result { + 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()) +} + +pub fn part_2(input: &str) -> Result { + 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", part_1(TEST_INPUT)?)) + } + + #[test] + fn test_part_2_example() -> Result<()> { + Ok(assert_eq!("MCD", part_2(TEST_INPUT)?)) + } +} -- cgit v1.2.3-70-g09d2