diff --git a/day05/common.rs b/day05/common.rs
new file mode 100644
index 0000000..facbef5
--- /dev/null
+++ b/day05/common.rs
@@ -0,0 +1,154 @@
+// Copyright 2022 Christian Ulrich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use std::io;
+
+#[derive(Debug)]
+pub struct Stacks {
+ inner: Vec>,
+}
+
+pub struct Moves {}
+
+#[derive(Debug)]
+pub struct Move {
+ count: usize,
+ from: usize,
+ to: usize,
+}
+
+impl Stacks {
+ pub fn from_stdin() -> Result {
+ let mut stacks = vec![];
+ for line in io::stdin().lines() {
+ match Self::parse_line(&line.unwrap())? {
+ None => break,
+ Some(items) => {
+ if items.len() != stacks.len() {
+ if !stacks.is_empty() {
+ return Err("input file contains invalid line");
+ }
+ stacks = vec![vec![]; items.len()];
+ }
+ for i in 0..items.len() {
+ if let Some(item) = items[i] {
+ stacks[i].push(item);
+ }
+ }
+ }
+ }
+ }
+ for stack in stacks.iter_mut() {
+ stack.reverse();
+ }
+ Ok(Self { inner: stacks })
+ }
+
+ fn parse_line(input: &str) -> Result>>, &'static str> {
+ if input.starts_with(" 1 ") {
+ return Ok(None);
+ }
+ let mut result = vec![];
+ for chunk in input.chars().collect::>().chunks(4) {
+ match chunk.iter().collect::().as_str() {
+ "" => break,
+ item => result.push(Self::parse_item(item)?),
+ }
+ }
+ Ok(Some(result))
+ }
+
+ fn parse_item(input: &str) -> Result, &'static str> {
+ match input.trim() {
+ "" => return Ok(None),
+ item => {
+ if item.len() == 3
+ && item.chars().nth(0).unwrap() == '['
+ && item.chars().nth(2).unwrap() == ']'
+ {
+ return Ok(item.chars().nth(1));
+ }
+ }
+ }
+ Err("cannot parse item")
+ }
+
+ pub fn move_items(&mut self, spec: Move) -> Result<(), &'static str> {
+ if spec.from >= self.inner.len()
+ || spec.to >= self.inner.len()
+ || spec.count > self.inner[spec.from].len()
+ {
+ return Err("invalid move");
+ }
+ for _ in 0..spec.count {
+ let item = self.inner[spec.from].pop().unwrap();
+ self.inner[spec.to].push(item);
+ }
+ Ok(())
+ }
+}
+
+impl Iterator for Stacks {
+ type Item = Vec;
+
+ fn next(&mut self) -> Option {
+ match self.inner.len() {
+ 0 => None,
+ _ => Some(self.inner.remove(0)),
+ }
+ }
+}
+
+impl Move {
+ pub fn from_str(input: &str) -> Result {
+ let parts = input.split(' ').collect::>();
+ if parts.len() != 6 || parts[0] != "move" || parts[2] != "from" || parts[4] != "to" {
+ return Err("cannot parse move");
+ }
+ let from = match parts[3].parse::().map_err(|_| "cannot parse move")? {
+ value if { value > 0 } => Ok(value - 1),
+ _ => Err("integer out of range"),
+ };
+ let to = match parts[5].parse::().map_err(|_| "cannot parse move")? {
+ value if { value > 0 } => Ok(value - 1),
+ _ => Err("integer out of range"),
+ };
+ Ok(Self {
+ count: parts[1].parse().map_err(|_| "cannot parse integer")?,
+ from: from?,
+ to: to?,
+ })
+ }
+}
+
+impl Moves {
+ pub fn from_stdin() -> Self {
+ Self {}
+ }
+}
+
+impl Iterator for Moves {
+ type Item = Result;
+
+ fn next(&mut self) -> Option {
+ for line in io::stdin().lines() {
+ let line = line.unwrap();
+ if line.len() > 0 {
+ return Some(Move::from_str(&line));
+ }
+ }
+ None
+ }
+}
diff --git a/day05/input b/day05/input
new file mode 100644
index 0000000..924a9a2
--- /dev/null
+++ b/day05/input
@@ -0,0 +1,516 @@
+ [G] [W] [Q]
+[Z] [Q] [M] [J] [F]
+[V] [V] [S] [F] [N] [R]
+[T] [F] [C] [H] [F] [W] [P]
+[B] [L] [L] [J] [C] [V] [D] [V]
+[J] [V] [F] [N] [T] [T] [C] [Z] [W]
+[G] [R] [Q] [H] [Q] [W] [Z] [G] [B]
+[R] [J] [S] [Z] [R] [S] [D] [L] [J]
+ 1 2 3 4 5 6 7 8 9
+
+move 6 from 5 to 7
+move 2 from 9 to 1
+move 4 from 8 to 6
+move 1 from 8 to 1
+move 2 from 9 to 1
+move 1 from 6 to 1
+move 13 from 7 to 8
+move 1 from 2 to 8
+move 9 from 1 to 5
+move 1 from 3 to 8
+move 3 from 6 to 7
+move 4 from 4 to 1
+move 11 from 5 to 6
+move 6 from 6 to 9
+move 3 from 4 to 2
+move 7 from 8 to 6
+move 1 from 7 to 5
+move 1 from 4 to 3
+move 7 from 1 to 5
+move 2 from 2 to 7
+move 4 from 9 to 6
+move 1 from 3 to 6
+move 1 from 1 to 9
+move 1 from 3 to 6
+move 1 from 5 to 8
+move 4 from 6 to 7
+move 3 from 8 to 7
+move 7 from 5 to 7
+move 1 from 3 to 1
+move 1 from 2 to 6
+move 14 from 6 to 5
+move 2 from 5 to 2
+move 3 from 9 to 2
+move 6 from 2 to 9
+move 7 from 8 to 6
+move 7 from 7 to 3
+move 2 from 8 to 7
+move 6 from 3 to 7
+move 17 from 7 to 1
+move 1 from 3 to 1
+move 1 from 2 to 5
+move 4 from 5 to 6
+move 17 from 6 to 9
+move 7 from 9 to 4
+move 1 from 2 to 7
+move 2 from 5 to 4
+move 3 from 7 to 8
+move 7 from 5 to 2
+move 6 from 2 to 8
+move 8 from 9 to 6
+move 1 from 2 to 3
+move 8 from 4 to 9
+move 7 from 6 to 9
+move 18 from 1 to 7
+move 1 from 1 to 8
+move 2 from 6 to 9
+move 1 from 3 to 9
+move 1 from 4 to 6
+move 1 from 8 to 3
+move 1 from 3 to 1
+move 10 from 7 to 2
+move 9 from 8 to 4
+move 1 from 6 to 4
+move 2 from 7 to 8
+move 5 from 4 to 9
+move 17 from 9 to 5
+move 2 from 7 to 6
+move 5 from 9 to 7
+move 5 from 4 to 2
+move 8 from 2 to 4
+move 8 from 4 to 3
+move 2 from 6 to 5
+move 2 from 8 to 5
+move 3 from 9 to 3
+move 4 from 7 to 3
+move 6 from 9 to 6
+move 4 from 6 to 9
+move 5 from 9 to 3
+move 8 from 5 to 2
+move 1 from 1 to 9
+move 1 from 6 to 3
+move 1 from 9 to 4
+move 5 from 7 to 4
+move 19 from 3 to 1
+move 4 from 2 to 8
+move 13 from 5 to 1
+move 1 from 6 to 3
+move 3 from 3 to 6
+move 2 from 8 to 9
+move 4 from 2 to 9
+move 2 from 2 to 6
+move 1 from 1 to 6
+move 5 from 1 to 9
+move 10 from 9 to 3
+move 15 from 1 to 6
+move 21 from 6 to 2
+move 20 from 2 to 1
+move 2 from 8 to 9
+move 28 from 1 to 2
+move 6 from 4 to 6
+move 2 from 1 to 5
+move 3 from 3 to 4
+move 2 from 5 to 4
+move 1 from 4 to 3
+move 3 from 4 to 5
+move 2 from 5 to 4
+move 1 from 1 to 8
+move 25 from 2 to 9
+move 1 from 4 to 6
+move 1 from 3 to 8
+move 4 from 3 to 6
+move 1 from 4 to 9
+move 2 from 6 to 3
+move 1 from 5 to 9
+move 5 from 2 to 8
+move 7 from 9 to 6
+move 2 from 9 to 4
+move 3 from 2 to 1
+move 3 from 3 to 4
+move 1 from 3 to 5
+move 16 from 6 to 3
+move 7 from 8 to 3
+move 5 from 4 to 3
+move 1 from 1 to 3
+move 1 from 2 to 6
+move 1 from 5 to 6
+move 21 from 3 to 5
+move 2 from 1 to 2
+move 1 from 6 to 7
+move 10 from 9 to 8
+move 1 from 6 to 5
+move 5 from 8 to 7
+move 12 from 5 to 3
+move 20 from 3 to 6
+move 4 from 7 to 9
+move 1 from 7 to 3
+move 1 from 2 to 5
+move 1 from 3 to 8
+move 2 from 8 to 4
+move 4 from 8 to 7
+move 3 from 6 to 1
+move 1 from 1 to 5
+move 2 from 9 to 2
+move 2 from 1 to 5
+move 2 from 5 to 6
+move 3 from 7 to 1
+move 2 from 1 to 4
+move 4 from 6 to 8
+move 3 from 4 to 7
+move 3 from 2 to 5
+move 2 from 7 to 9
+move 9 from 9 to 8
+move 1 from 4 to 1
+move 7 from 5 to 7
+move 1 from 7 to 8
+move 1 from 3 to 1
+move 4 from 7 to 5
+move 2 from 1 to 9
+move 1 from 1 to 2
+move 5 from 5 to 4
+move 1 from 2 to 6
+move 5 from 7 to 9
+move 5 from 4 to 7
+move 11 from 9 to 6
+move 14 from 8 to 9
+move 23 from 6 to 5
+move 6 from 9 to 5
+move 1 from 6 to 2
+move 10 from 5 to 3
+move 1 from 4 to 9
+move 1 from 2 to 1
+move 2 from 7 to 3
+move 10 from 5 to 7
+move 8 from 5 to 2
+move 5 from 3 to 5
+move 7 from 5 to 8
+move 1 from 2 to 7
+move 9 from 7 to 9
+move 3 from 2 to 3
+move 2 from 6 to 2
+move 2 from 3 to 6
+move 4 from 7 to 5
+move 1 from 1 to 5
+move 4 from 3 to 1
+move 2 from 5 to 2
+move 1 from 3 to 2
+move 2 from 6 to 8
+move 7 from 5 to 3
+move 9 from 2 to 4
+move 2 from 1 to 2
+move 2 from 5 to 3
+move 1 from 4 to 9
+move 1 from 6 to 9
+move 1 from 4 to 2
+move 2 from 1 to 7
+move 3 from 2 to 6
+move 4 from 8 to 7
+move 2 from 8 to 3
+move 2 from 3 to 7
+move 1 from 6 to 5
+move 2 from 8 to 2
+move 5 from 4 to 1
+move 8 from 9 to 8
+move 1 from 5 to 7
+move 10 from 9 to 2
+move 8 from 8 to 2
+move 1 from 1 to 6
+move 12 from 3 to 9
+move 7 from 7 to 4
+move 13 from 2 to 4
+move 7 from 2 to 7
+move 1 from 6 to 7
+move 3 from 9 to 8
+move 2 from 6 to 3
+move 1 from 3 to 2
+move 1 from 3 to 9
+move 3 from 1 to 5
+move 1 from 1 to 6
+move 4 from 7 to 6
+move 5 from 7 to 1
+move 1 from 2 to 1
+move 6 from 9 to 4
+move 5 from 9 to 7
+move 3 from 8 to 3
+move 22 from 4 to 9
+move 24 from 9 to 8
+move 1 from 9 to 2
+move 2 from 4 to 3
+move 10 from 8 to 3
+move 1 from 2 to 1
+move 1 from 3 to 8
+move 1 from 6 to 3
+move 1 from 1 to 4
+move 4 from 3 to 4
+move 4 from 6 to 1
+move 2 from 4 to 5
+move 4 from 7 to 2
+move 7 from 4 to 6
+move 4 from 6 to 1
+move 2 from 6 to 3
+move 1 from 6 to 2
+move 5 from 5 to 2
+move 12 from 3 to 5
+move 3 from 7 to 8
+move 6 from 2 to 3
+move 11 from 1 to 9
+move 1 from 1 to 7
+move 1 from 7 to 5
+move 2 from 3 to 9
+move 2 from 9 to 7
+move 4 from 2 to 5
+move 2 from 7 to 1
+move 17 from 8 to 1
+move 1 from 3 to 2
+move 16 from 1 to 3
+move 8 from 3 to 4
+move 2 from 8 to 3
+move 2 from 1 to 5
+move 1 from 2 to 6
+move 12 from 5 to 8
+move 1 from 6 to 3
+move 9 from 3 to 9
+move 8 from 4 to 6
+move 2 from 1 to 6
+move 6 from 8 to 4
+move 3 from 4 to 6
+move 1 from 1 to 9
+move 11 from 6 to 8
+move 3 from 4 to 3
+move 17 from 9 to 5
+move 2 from 6 to 7
+move 1 from 9 to 1
+move 2 from 8 to 6
+move 1 from 7 to 5
+move 1 from 8 to 9
+move 1 from 1 to 7
+move 3 from 9 to 6
+move 2 from 7 to 8
+move 1 from 9 to 6
+move 15 from 5 to 2
+move 9 from 3 to 9
+move 11 from 8 to 3
+move 6 from 9 to 8
+move 4 from 6 to 7
+move 3 from 3 to 7
+move 5 from 5 to 6
+move 7 from 7 to 5
+move 3 from 6 to 1
+move 2 from 1 to 4
+move 1 from 9 to 2
+move 2 from 9 to 3
+move 2 from 6 to 3
+move 1 from 1 to 8
+move 6 from 5 to 9
+move 8 from 2 to 5
+move 10 from 8 to 5
+move 1 from 2 to 9
+move 21 from 5 to 9
+move 2 from 8 to 4
+move 5 from 9 to 1
+move 2 from 5 to 2
+move 15 from 9 to 2
+move 1 from 5 to 9
+move 9 from 9 to 3
+move 1 from 1 to 6
+move 3 from 4 to 1
+move 20 from 3 to 5
+move 20 from 5 to 4
+move 7 from 4 to 3
+move 1 from 1 to 7
+move 11 from 4 to 5
+move 4 from 3 to 2
+move 11 from 5 to 4
+move 2 from 6 to 7
+move 4 from 3 to 9
+move 2 from 2 to 8
+move 2 from 9 to 4
+move 6 from 4 to 6
+move 2 from 7 to 9
+move 1 from 7 to 6
+move 1 from 4 to 9
+move 4 from 4 to 6
+move 2 from 8 to 6
+move 1 from 4 to 3
+move 1 from 4 to 6
+move 1 from 3 to 1
+move 3 from 4 to 3
+move 9 from 2 to 8
+move 2 from 3 to 7
+move 5 from 6 to 2
+move 2 from 7 to 5
+move 1 from 5 to 2
+move 1 from 9 to 3
+move 1 from 5 to 1
+move 13 from 2 to 5
+move 4 from 9 to 5
+move 1 from 3 to 4
+move 9 from 2 to 3
+move 7 from 3 to 2
+move 11 from 5 to 6
+move 5 from 8 to 7
+move 1 from 3 to 1
+move 2 from 8 to 5
+move 2 from 8 to 1
+move 1 from 4 to 1
+move 6 from 2 to 7
+move 3 from 5 to 3
+move 1 from 2 to 5
+move 7 from 7 to 9
+move 3 from 3 to 5
+move 1 from 2 to 5
+move 2 from 3 to 2
+move 6 from 1 to 7
+move 10 from 7 to 3
+move 1 from 2 to 3
+move 6 from 9 to 8
+move 1 from 2 to 4
+move 2 from 6 to 1
+move 5 from 1 to 9
+move 8 from 5 to 8
+move 2 from 1 to 6
+move 6 from 3 to 4
+move 1 from 5 to 3
+move 4 from 9 to 6
+move 1 from 1 to 4
+move 2 from 9 to 2
+move 5 from 6 to 1
+move 11 from 6 to 7
+move 1 from 2 to 8
+move 6 from 7 to 5
+move 10 from 8 to 4
+move 2 from 3 to 9
+move 3 from 3 to 5
+move 4 from 7 to 9
+move 2 from 1 to 3
+move 10 from 5 to 8
+move 6 from 6 to 1
+move 2 from 6 to 8
+move 2 from 9 to 5
+move 4 from 9 to 6
+move 7 from 4 to 8
+move 5 from 6 to 1
+move 4 from 8 to 2
+move 2 from 5 to 6
+move 5 from 4 to 5
+move 1 from 7 to 5
+move 2 from 3 to 6
+move 1 from 3 to 8
+move 4 from 6 to 1
+move 4 from 2 to 3
+move 5 from 5 to 1
+move 2 from 3 to 2
+move 2 from 3 to 2
+move 20 from 8 to 2
+move 5 from 4 to 8
+move 1 from 4 to 3
+move 8 from 2 to 1
+move 1 from 5 to 6
+move 5 from 2 to 3
+move 1 from 6 to 5
+move 5 from 3 to 2
+move 1 from 3 to 7
+move 6 from 8 to 5
+move 13 from 2 to 9
+move 7 from 9 to 8
+move 1 from 7 to 8
+move 5 from 8 to 3
+move 2 from 2 to 5
+move 2 from 8 to 4
+move 27 from 1 to 5
+move 1 from 2 to 3
+move 5 from 3 to 1
+move 22 from 5 to 7
+move 1 from 8 to 5
+move 1 from 3 to 2
+move 7 from 1 to 3
+move 2 from 3 to 7
+move 2 from 2 to 4
+move 5 from 9 to 1
+move 5 from 3 to 9
+move 3 from 1 to 5
+move 3 from 1 to 6
+move 3 from 6 to 3
+move 4 from 4 to 2
+move 8 from 5 to 3
+move 8 from 7 to 4
+move 14 from 7 to 4
+move 1 from 1 to 7
+move 6 from 9 to 6
+move 7 from 5 to 3
+move 14 from 3 to 6
+move 2 from 2 to 1
+move 4 from 3 to 7
+move 6 from 7 to 6
+move 1 from 7 to 6
+move 1 from 5 to 1
+move 2 from 1 to 5
+move 3 from 5 to 7
+move 8 from 6 to 5
+move 5 from 5 to 1
+move 1 from 7 to 3
+move 1 from 3 to 8
+move 22 from 4 to 7
+move 7 from 6 to 3
+move 4 from 3 to 2
+move 3 from 1 to 3
+move 17 from 7 to 6
+move 1 from 8 to 1
+move 2 from 2 to 4
+move 3 from 7 to 2
+move 2 from 2 to 9
+move 1 from 1 to 8
+move 2 from 3 to 1
+move 6 from 6 to 8
+move 2 from 9 to 2
+move 4 from 5 to 1
+move 5 from 8 to 9
+move 1 from 7 to 3
+move 4 from 3 to 4
+move 1 from 7 to 4
+move 4 from 9 to 7
+move 5 from 7 to 9
+move 1 from 7 to 3
+move 2 from 2 to 8
+move 5 from 4 to 2
+move 21 from 6 to 8
+move 2 from 3 to 8
+move 23 from 8 to 6
+move 1 from 2 to 6
+move 2 from 9 to 8
+move 22 from 6 to 7
+move 2 from 9 to 3
+move 2 from 3 to 7
+move 2 from 1 to 6
+move 1 from 2 to 5
+move 3 from 1 to 3
+move 6 from 7 to 4
+move 5 from 8 to 5
+move 1 from 3 to 8
+move 1 from 9 to 3
+move 6 from 4 to 8
+move 1 from 5 to 3
+move 6 from 2 to 8
+move 15 from 7 to 5
+move 1 from 7 to 1
+move 14 from 5 to 8
+move 1 from 4 to 9
+move 5 from 1 to 7
+move 3 from 6 to 2
+move 4 from 5 to 6
+move 1 from 4 to 8
+move 4 from 3 to 1
+move 2 from 9 to 2
+move 7 from 7 to 1
+move 7 from 2 to 7
+move 9 from 8 to 6
+move 7 from 7 to 1
+move 12 from 6 to 8
+move 25 from 8 to 6
+move 3 from 8 to 1
+move 28 from 6 to 2
+move 15 from 2 to 3
+move 1 from 5 to 4
+move 3 from 2 to 7
+move 6 from 2 to 9
+
diff --git a/day05/part1.rs b/day05/part1.rs
new file mode 100644
index 0000000..c23971a
--- /dev/null
+++ b/day05/part1.rs
@@ -0,0 +1,32 @@
+// Copyright 2022 Christian Ulrich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// usage: ./part1 < input
+
+pub mod common;
+
+use common::{Moves, Stacks};
+
+fn main() -> Result<(), &'static str> {
+ let mut stacks = Stacks::from_stdin()?;
+ for op in Moves::from_stdin() {
+ stacks.move_items(op?)?;
+ }
+ let result: Result = stacks
+ .map(|s| s.last().cloned().ok_or("at least one stack is empty"))
+ .collect();
+ println!("{}", result?);
+ Ok(())
+}
diff --git a/day05/testinput b/day05/testinput
new file mode 100644
index 0000000..84933bb
--- /dev/null
+++ b/day05/testinput
@@ -0,0 +1,9 @@
+ [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