summaryrefslogtreecommitdiffstats
path: root/src/day_05.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/day_05.rs')
-rw-r--r--src/day_05.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/day_05.rs b/src/day_05.rs
new file mode 100644
index 0000000..75ebdff
--- /dev/null
+++ b/src/day_05.rs
@@ -0,0 +1,212 @@
+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 Day05;
+
+impl Problem for Day05 {
+ const DAY: u8 = 5;
+
+ const INPUT: &'static str = include_str!("../input/day_5.txt");
+}
+
+impl Solution for Day05 {
+ 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", Day05::part_1(TEST_INPUT)?))
+ }
+
+ #[test]
+ fn test_part_2_example() -> Result<()> {
+ Ok(assert_eq!("MCD", Day05::part_2(TEST_INPUT)?))
+ }
+}