diff --git a/day09/common.rs b/day09/common.rs
new file mode 100644
index 0000000..acaba7d
--- /dev/null
+++ b/day09/common.rs
@@ -0,0 +1,142 @@
+// 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;
+use std::str::FromStr;
+
+#[derive(Clone, Copy)]
+enum Direction {
+ Up,
+ Right,
+ Down,
+ Left,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct RopeState {
+ head_pos: (isize, isize),
+ tail_pos: (isize, isize),
+}
+
+pub struct RopeStates {
+ direction: Direction,
+ count: isize,
+ state: RopeState,
+}
+
+impl FromStr for Direction {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result {
+ match s {
+ "U" => Ok(Self::Up),
+ "R" => Ok(Self::Right),
+ "D" => Ok(Self::Down),
+ "L" => Ok(Self::Left),
+ _ => Err("cannot parse direction"),
+ }
+ }
+}
+
+impl RopeState {
+ pub fn new() -> Self {
+ RopeState {
+ head_pos: (0, 0),
+ tail_pos: (0, 0),
+ }
+ }
+
+ pub fn head_pos(&self) -> (isize, isize) {
+ self.head_pos
+ }
+
+ pub fn tail_pos(&self) -> (isize, isize) {
+ self.tail_pos
+ }
+
+ fn adjust_tail(&mut self) {
+ let x_diff = self.head_pos.0.abs_diff(self.tail_pos.0);
+ let y_diff = self.head_pos.1.abs_diff(self.tail_pos.1);
+ let (move_x, move_y) = match (x_diff, y_diff) {
+ (2, 0) => (true, false),
+ (2, 1) => (true, true),
+ (0, 2) => (false, true),
+ (1, 2) => (true, true),
+ _ => (false, false),
+ };
+ if move_x {
+ match (self.head_pos.0, self.tail_pos.0) {
+ (h, t) if { h > t } => self.tail_pos.0 += 1,
+ (h, t) if { h < t } => self.tail_pos.0 -= 1,
+ _ => (),
+ }
+ }
+ if move_y {
+ match (self.head_pos.1, self.tail_pos.1) {
+ (h, t) if { h > t } => self.tail_pos.1 += 1,
+ (h, t) if { h < t } => self.tail_pos.1 -= 1,
+ _ => (),
+ }
+ }
+ }
+
+ fn move_head(&mut self, direction: Direction) -> Result {
+ const ERROR: &'static str = "invalid move";
+ match direction {
+ Direction::Up => self.head_pos.1 = self.head_pos.1.checked_add(1).ok_or(ERROR)?,
+ Direction::Right => self.head_pos.0 = self.head_pos.0.checked_add(1).ok_or(ERROR)?,
+ Direction::Down => self.head_pos.1 = self.head_pos.1.checked_sub(1).ok_or(ERROR)?,
+ Direction::Left => self.head_pos.0 = self.head_pos.0.checked_sub(1).ok_or(ERROR)?,
+ }
+ self.adjust_tail();
+ Ok(*self)
+ }
+}
+
+impl Iterator for RopeStates {
+ type Item = Result;
+
+ fn next(&mut self) -> Option {
+ if self.count == 0 {
+ let mut line = String::new();
+ match io::stdin().read_line(&mut line) {
+ Ok(_) if { line == "" || line == "\n" } => return None,
+ Ok(_) => {
+ if let Some((left, right)) = line.trim().split_once(' ') {
+ if let (Ok(direction), Ok(count)) = (left.parse(), right.parse()) {
+ self.direction = direction;
+ self.count = count;
+ } else {
+ return Some(Err("input file contains invalid line"));
+ }
+ } else {
+ return Some(Err("input file contains invalid line"));
+ }
+ }
+ Err(_) => assert!(false),
+ }
+ }
+ self.count -= 1;
+ Some(self.state.move_head(self.direction))
+ }
+}
+
+pub fn rope_states() -> RopeStates {
+ RopeStates {
+ direction: Direction::Up,
+ count: 0,
+ state: RopeState::new(),
+ }
+}
diff --git a/day09/input b/day09/input
new file mode 100644
index 0000000..1785c9e
--- /dev/null
+++ b/day09/input
@@ -0,0 +1,2001 @@
+L 1
+R 1
+U 1
+R 1
+L 1
+U 2
+L 2
+R 1
+U 2
+D 2
+R 2
+D 2
+R 1
+U 1
+R 2
+L 1
+D 1
+L 2
+R 1
+D 1
+R 1
+U 1
+D 1
+R 1
+D 1
+R 1
+D 2
+L 2
+D 1
+R 2
+L 1
+R 2
+U 2
+R 1
+U 1
+D 2
+U 1
+R 1
+D 1
+R 2
+D 1
+L 2
+R 1
+U 1
+L 1
+U 1
+D 1
+U 1
+D 1
+L 1
+D 1
+R 1
+D 2
+L 1
+D 1
+L 1
+D 1
+R 1
+U 1
+D 2
+R 1
+D 1
+L 2
+D 2
+U 1
+L 1
+U 1
+L 1
+R 2
+U 1
+L 1
+D 1
+R 2
+L 2
+U 1
+D 2
+R 1
+U 1
+R 2
+L 2
+U 1
+L 2
+R 2
+D 1
+L 1
+R 1
+D 2
+L 2
+U 1
+L 2
+R 1
+U 2
+D 1
+U 2
+D 2
+L 2
+D 1
+R 1
+L 2
+R 2
+L 2
+R 2
+L 2
+R 2
+L 1
+U 1
+R 2
+U 1
+D 1
+R 2
+D 2
+R 2
+D 2
+L 2
+R 2
+U 3
+R 1
+U 3
+D 3
+U 1
+L 2
+D 2
+R 3
+D 1
+R 3
+U 1
+L 3
+R 3
+L 1
+U 3
+R 3
+U 3
+R 3
+U 2
+L 2
+D 2
+R 3
+U 2
+L 2
+U 3
+L 2
+R 1
+U 3
+R 2
+U 3
+L 2
+R 2
+U 1
+L 3
+R 3
+L 3
+R 2
+L 3
+D 3
+L 2
+D 1
+R 1
+D 3
+U 3
+R 3
+D 2
+L 2
+D 2
+R 1
+D 1
+U 3
+R 1
+U 2
+L 3
+U 3
+L 2
+D 1
+L 3
+R 2
+D 2
+L 2
+D 3
+L 3
+U 1
+D 3
+U 3
+L 3
+R 2
+D 2
+R 3
+D 2
+R 3
+U 2
+R 1
+L 2
+R 2
+L 1
+D 3
+U 2
+L 2
+U 3
+L 1
+D 1
+L 2
+R 3
+L 1
+D 2
+U 1
+D 3
+U 1
+D 3
+L 1
+U 1
+R 3
+L 2
+U 3
+L 3
+U 1
+L 2
+D 1
+R 2
+L 1
+R 2
+L 2
+D 3
+U 2
+D 2
+U 3
+L 4
+D 4
+R 3
+D 4
+R 1
+L 2
+U 2
+R 1
+U 4
+D 4
+U 4
+D 4
+R 4
+L 2
+U 3
+D 1
+R 4
+D 2
+L 2
+D 1
+L 4
+R 3
+U 2
+D 4
+U 3
+R 3
+L 1
+R 1
+L 2
+D 4
+U 2
+R 1
+L 3
+D 3
+R 4
+U 2
+L 1
+D 3
+R 4
+D 2
+U 3
+D 1
+L 2
+D 2
+R 2
+D 3
+R 2
+U 2
+R 1
+L 1
+U 2
+D 2
+U 3
+D 1
+U 3
+D 1
+R 4
+D 2
+U 1
+L 3
+D 4
+R 3
+D 4
+U 2
+D 1
+R 2
+U 3
+L 3
+D 1
+U 2
+L 3
+D 1
+L 3
+D 1
+U 1
+D 4
+R 1
+D 4
+L 2
+R 3
+D 4
+R 2
+L 3
+D 4
+U 3
+L 4
+U 1
+L 3
+R 1
+L 1
+D 2
+L 1
+U 2
+D 3
+L 1
+R 4
+L 1
+D 2
+U 2
+L 3
+U 3
+L 1
+R 4
+U 3
+D 3
+R 1
+L 2
+R 4
+U 2
+L 3
+U 4
+L 2
+R 4
+D 5
+U 1
+R 2
+D 5
+L 5
+R 1
+D 3
+U 3
+L 3
+U 5
+R 1
+D 1
+R 3
+U 3
+D 1
+R 1
+L 4
+R 1
+L 3
+U 4
+L 3
+R 5
+D 3
+R 2
+U 1
+R 2
+D 2
+L 4
+R 2
+U 1
+L 3
+D 4
+R 4
+L 1
+R 3
+U 3
+D 2
+R 3
+D 3
+U 1
+L 5
+U 1
+L 5
+R 5
+D 1
+U 1
+R 5
+D 4
+L 1
+R 4
+L 5
+D 5
+L 2
+D 1
+L 3
+D 5
+L 4
+R 4
+D 1
+U 1
+D 4
+U 1
+R 4
+L 2
+D 4
+U 1
+L 3
+R 3
+L 3
+R 3
+D 2
+R 3
+L 5
+R 1
+L 1
+R 3
+D 5
+U 5
+L 3
+U 3
+L 5
+D 3
+L 2
+U 5
+D 5
+U 5
+D 4
+U 4
+R 5
+U 3
+D 5
+R 1
+L 4
+U 1
+L 4
+R 2
+D 1
+U 4
+L 5
+D 2
+R 1
+U 1
+R 3
+U 1
+R 4
+U 4
+L 1
+R 2
+U 5
+D 2
+L 5
+U 4
+D 2
+R 1
+D 1
+U 6
+D 5
+L 4
+U 5
+R 1
+L 3
+D 2
+R 3
+U 6
+L 2
+R 6
+D 1
+R 5
+D 1
+L 1
+R 2
+L 4
+R 3
+L 4
+D 6
+R 5
+U 1
+L 4
+D 2
+L 2
+D 3
+L 2
+D 5
+R 4
+D 3
+L 2
+D 4
+R 1
+U 5
+L 5
+U 5
+D 2
+L 3
+D 4
+L 3
+U 4
+L 4
+U 6
+D 6
+U 4
+R 2
+D 3
+U 1
+R 4
+D 3
+R 1
+D 2
+R 3
+L 2
+D 2
+L 4
+D 5
+L 5
+R 2
+L 1
+R 2
+D 2
+R 4
+U 4
+R 5
+L 4
+D 1
+L 6
+R 4
+D 2
+R 6
+L 6
+D 3
+R 2
+D 2
+U 4
+R 2
+U 2
+L 2
+R 5
+D 5
+U 6
+D 6
+L 5
+R 1
+U 3
+L 1
+D 1
+U 2
+R 5
+L 2
+D 2
+U 4
+R 2
+D 2
+U 1
+D 2
+R 3
+L 6
+U 1
+D 6
+R 3
+D 6
+R 1
+D 7
+R 7
+L 4
+R 4
+D 5
+U 6
+R 2
+L 3
+R 5
+L 5
+U 6
+D 3
+U 5
+D 3
+U 5
+R 7
+L 5
+U 7
+L 6
+U 5
+D 1
+L 7
+U 6
+L 3
+D 1
+L 6
+U 4
+D 1
+U 1
+R 1
+L 3
+D 6
+L 6
+R 3
+D 3
+U 2
+R 1
+D 6
+U 7
+R 6
+L 4
+R 4
+D 7
+R 1
+D 5
+U 6
+D 4
+R 6
+L 2
+U 4
+D 7
+R 5
+D 6
+U 7
+R 6
+D 6
+U 1
+D 4
+L 2
+U 2
+R 7
+U 1
+L 1
+U 1
+L 7
+D 3
+U 5
+D 5
+L 1
+D 4
+L 3
+U 7
+L 6
+R 7
+U 6
+R 1
+L 3
+R 1
+D 3
+L 7
+R 4
+L 3
+D 2
+L 7
+D 2
+R 5
+L 7
+D 6
+L 6
+D 3
+L 3
+D 5
+U 3
+L 5
+R 6
+L 1
+U 5
+R 4
+U 2
+D 3
+R 5
+L 4
+U 5
+D 7
+R 1
+L 3
+U 5
+D 3
+L 6
+R 2
+L 2
+U 6
+R 3
+U 7
+R 3
+D 1
+L 2
+U 7
+L 6
+D 3
+L 8
+R 6
+D 3
+L 5
+R 5
+U 5
+R 8
+D 7
+R 7
+U 8
+L 5
+D 6
+R 6
+L 6
+D 8
+U 1
+R 4
+L 3
+R 6
+L 6
+D 5
+R 4
+D 8
+L 4
+R 4
+D 3
+L 2
+U 6
+L 8
+R 1
+U 5
+L 4
+D 8
+R 6
+D 5
+U 6
+L 5
+R 1
+L 8
+U 7
+R 6
+D 4
+U 2
+D 8
+U 3
+D 7
+R 6
+D 8
+R 8
+U 8
+D 7
+L 3
+D 4
+L 1
+U 4
+R 4
+U 2
+D 6
+R 1
+D 8
+R 2
+D 4
+R 5
+L 1
+R 7
+L 3
+D 1
+U 1
+R 7
+U 5
+D 1
+L 2
+R 8
+D 8
+L 5
+U 8
+D 4
+L 5
+D 3
+U 2
+L 7
+R 2
+L 5
+U 3
+R 3
+U 6
+R 6
+D 4
+R 3
+U 4
+D 5
+U 6
+R 3
+L 4
+R 5
+L 4
+R 8
+U 6
+L 6
+U 1
+L 5
+R 3
+U 1
+D 5
+U 8
+R 1
+L 2
+R 3
+L 8
+U 6
+L 4
+R 3
+L 3
+R 7
+L 9
+D 5
+R 8
+L 5
+U 7
+D 7
+R 6
+L 8
+U 4
+D 5
+R 8
+L 4
+R 8
+U 9
+R 9
+L 8
+D 9
+L 9
+D 6
+R 1
+L 8
+U 4
+L 6
+U 5
+R 9
+L 5
+D 6
+R 9
+D 9
+U 6
+L 1
+U 8
+L 7
+R 9
+U 4
+R 6
+U 1
+R 5
+U 5
+L 4
+R 4
+D 7
+L 6
+U 3
+R 9
+U 1
+L 6
+U 5
+L 5
+U 2
+L 3
+U 2
+D 6
+R 8
+L 5
+U 4
+D 4
+R 4
+D 6
+L 8
+U 9
+R 4
+L 3
+D 1
+U 2
+D 5
+U 3
+D 3
+L 2
+D 3
+L 4
+D 5
+L 9
+D 9
+U 2
+D 8
+R 4
+L 8
+R 9
+D 7
+L 3
+D 2
+R 2
+L 4
+D 7
+R 6
+D 8
+R 9
+L 3
+D 5
+U 1
+L 8
+D 6
+R 9
+U 6
+R 9
+D 6
+L 9
+U 5
+D 10
+L 4
+U 10
+R 3
+U 7
+D 4
+L 3
+R 8
+L 4
+U 10
+R 8
+L 10
+U 8
+R 5
+L 8
+U 6
+L 5
+D 7
+L 6
+U 6
+L 9
+R 8
+U 6
+D 7
+L 10
+R 2
+D 7
+L 8
+U 5
+L 2
+D 2
+U 7
+D 6
+R 7
+D 2
+U 3
+R 10
+D 2
+U 6
+D 6
+L 8
+D 2
+L 2
+D 10
+L 2
+D 1
+L 10
+D 6
+R 6
+L 7
+D 9
+R 2
+D 5
+U 2
+R 4
+L 4
+D 1
+L 3
+R 10
+U 9
+L 4
+D 10
+L 10
+D 4
+U 2
+R 1
+L 4
+D 6
+U 2
+L 6
+R 4
+U 1
+L 9
+R 7
+L 8
+U 5
+R 1
+D 6
+L 4
+U 9
+D 1
+L 5
+R 10
+D 4
+L 6
+U 8
+R 4
+L 5
+D 10
+R 8
+D 8
+R 4
+D 5
+R 7
+L 10
+D 3
+R 1
+L 4
+R 8
+L 10
+D 2
+L 3
+D 6
+L 8
+R 10
+L 2
+D 8
+R 5
+L 8
+D 1
+U 6
+D 11
+U 7
+L 1
+U 6
+R 9
+L 8
+R 5
+U 9
+D 5
+L 10
+U 6
+L 3
+D 4
+U 6
+D 10
+L 2
+D 9
+L 5
+R 8
+D 1
+R 1
+U 1
+D 4
+R 4
+U 6
+L 9
+U 7
+R 11
+L 8
+D 3
+R 9
+L 11
+U 9
+L 4
+D 10
+U 9
+L 7
+R 9
+U 1
+R 8
+U 4
+L 3
+U 2
+D 7
+R 6
+U 1
+D 10
+U 9
+R 6
+L 6
+D 4
+U 3
+L 10
+R 10
+U 6
+R 8
+L 9
+R 2
+D 7
+R 4
+U 6
+R 2
+L 4
+D 1
+L 10
+U 9
+R 11
+U 11
+R 1
+D 5
+L 8
+U 3
+R 1
+U 4
+D 10
+U 6
+L 8
+R 4
+L 7
+R 8
+D 3
+L 9
+U 4
+L 6
+R 2
+D 2
+U 9
+D 6
+R 8
+L 2
+U 7
+L 2
+R 8
+L 7
+D 5
+R 10
+U 5
+L 10
+U 6
+R 8
+U 9
+L 7
+D 11
+U 10
+L 4
+D 5
+R 4
+L 7
+U 5
+D 8
+R 6
+D 9
+L 4
+U 9
+R 6
+D 1
+U 6
+R 8
+U 10
+D 6
+U 10
+L 3
+D 1
+L 1
+R 9
+D 10
+L 10
+U 12
+D 3
+L 2
+U 6
+L 9
+U 2
+R 6
+D 8
+R 11
+L 1
+U 3
+R 1
+U 9
+R 12
+U 3
+D 11
+U 3
+D 10
+U 12
+L 11
+R 9
+U 9
+D 11
+L 8
+D 4
+R 4
+U 4
+D 1
+L 7
+R 10
+L 4
+R 4
+U 11
+D 3
+L 6
+R 10
+L 9
+R 11
+U 6
+D 2
+U 2
+L 3
+D 11
+U 6
+D 12
+L 5
+R 5
+D 6
+R 5
+U 2
+R 8
+L 7
+D 7
+R 3
+D 8
+U 6
+D 1
+U 1
+D 4
+L 3
+D 12
+L 8
+D 8
+U 7
+R 1
+D 10
+L 5
+D 7
+L 1
+U 4
+R 12
+L 8
+U 2
+L 2
+U 1
+L 8
+U 3
+L 12
+R 3
+D 11
+R 1
+U 4
+D 12
+L 3
+U 2
+R 5
+U 2
+R 7
+U 8
+L 5
+R 1
+D 5
+R 12
+D 8
+R 9
+D 7
+L 2
+R 2
+U 9
+D 6
+U 6
+R 2
+D 2
+R 9
+D 4
+R 12
+D 13
+R 9
+L 3
+D 8
+L 13
+U 2
+R 10
+U 12
+D 12
+U 3
+L 6
+D 8
+U 1
+L 12
+D 9
+L 7
+R 2
+D 4
+R 5
+L 7
+R 12
+U 4
+L 7
+D 4
+U 10
+L 2
+D 1
+U 5
+R 5
+U 2
+L 9
+D 10
+L 1
+R 7
+L 6
+D 1
+L 12
+R 6
+U 10
+R 2
+D 10
+R 5
+L 3
+R 8
+D 1
+R 12
+L 7
+U 5
+L 11
+D 4
+U 11
+D 1
+U 11
+L 1
+D 11
+U 11
+D 4
+U 8
+R 5
+D 6
+L 12
+R 5
+D 10
+L 2
+D 5
+R 10
+U 6
+D 6
+R 4
+D 7
+L 2
+D 5
+U 2
+R 9
+L 11
+U 13
+D 9
+L 11
+D 8
+L 2
+D 4
+R 13
+L 5
+U 12
+R 11
+U 5
+L 8
+U 5
+D 7
+U 1
+R 9
+D 4
+U 3
+R 9
+L 3
+D 7
+R 8
+D 7
+L 2
+U 4
+L 12
+U 13
+L 12
+R 8
+L 3
+R 12
+L 4
+U 12
+R 13
+D 10
+L 8
+U 12
+R 5
+L 1
+U 2
+L 3
+U 2
+D 12
+L 12
+U 11
+L 2
+R 6
+U 3
+L 2
+R 7
+U 7
+L 3
+R 8
+L 11
+D 2
+U 1
+R 12
+D 1
+R 4
+L 13
+U 14
+R 14
+D 12
+R 12
+D 11
+L 11
+U 2
+L 6
+D 8
+L 5
+D 7
+R 2
+L 14
+U 12
+R 13
+L 7
+D 2
+U 4
+D 5
+R 5
+L 7
+R 7
+U 1
+R 10
+D 11
+L 7
+U 2
+R 5
+L 11
+U 11
+R 9
+U 9
+R 12
+D 14
+R 5
+L 9
+U 2
+D 12
+L 6
+U 7
+R 5
+L 10
+D 10
+U 4
+D 13
+L 13
+D 3
+R 8
+D 10
+U 12
+L 14
+U 7
+D 6
+R 4
+U 11
+R 2
+L 5
+D 12
+R 4
+L 4
+D 5
+L 12
+U 3
+L 3
+U 7
+R 4
+L 2
+R 7
+L 5
+D 5
+R 9
+L 5
+D 12
+R 3
+U 1
+D 6
+U 2
+R 10
+L 4
+U 12
+R 15
+L 2
+R 3
+D 8
+L 13
+D 3
+R 5
+D 5
+L 5
+U 7
+L 4
+R 10
+D 11
+L 3
+R 1
+U 4
+D 15
+L 8
+U 10
+L 5
+D 1
+R 5
+D 15
+R 3
+U 9
+R 4
+D 3
+L 15
+U 6
+D 10
+U 2
+R 6
+U 10
+L 12
+R 8
+D 12
+R 14
+U 10
+D 1
+L 1
+D 8
+U 3
+R 11
+D 7
+L 5
+D 14
+R 1
+D 8
+R 6
+U 6
+D 1
+U 14
+R 12
+L 5
+U 5
+L 14
+U 1
+D 3
+R 6
+U 4
+R 9
+L 13
+R 2
+U 10
+R 11
+U 7
+D 8
+U 14
+L 4
+R 3
+U 6
+R 2
+D 14
+U 10
+D 13
+U 8
+R 12
+L 12
+U 6
+L 4
+U 5
+D 6
+U 8
+L 12
+D 1
+L 3
+R 4
+L 11
+R 5
+D 3
+U 6
+D 15
+R 14
+U 8
+R 10
+U 15
+R 13
+L 13
+U 4
+D 3
+U 4
+D 1
+U 5
+R 5
+D 4
+R 2
+D 13
+R 7
+D 10
+U 3
+R 15
+D 3
+U 13
+R 16
+U 2
+D 6
+L 13
+R 8
+L 5
+R 1
+U 7
+R 6
+U 6
+R 10
+U 5
+L 14
+R 12
+U 8
+R 13
+U 4
+L 10
+R 16
+L 15
+D 7
+R 14
+D 8
+U 8
+L 15
+R 2
+U 11
+L 6
+R 6
+D 2
+U 9
+D 12
+L 10
+U 7
+R 8
+L 4
+D 5
+R 13
+L 9
+U 15
+D 11
+R 11
+D 8
+L 10
+R 14
+D 3
+R 10
+U 1
+R 11
+L 16
+R 12
+D 15
+L 13
+U 2
+L 11
+U 15
+D 9
+R 1
+D 13
+L 16
+U 9
+D 2
+R 5
+U 9
+R 11
+L 16
+U 5
+R 11
+U 8
+L 15
+U 1
+L 11
+U 3
+L 4
+U 9
+L 5
+U 11
+D 9
+U 4
+L 14
+U 8
+L 16
+D 9
+U 5
+L 14
+R 13
+U 8
+L 4
+R 9
+L 4
+R 7
+D 15
+U 14
+D 8
+U 6
+R 11
+L 15
+D 10
+R 2
+L 12
+R 11
+D 7
+R 11
+L 4
+R 4
+D 4
+R 3
+U 15
+L 6
+R 8
+L 5
+U 13
+D 6
+R 1
+D 15
+L 9
+U 2
+L 13
+R 10
+U 8
+R 15
+D 16
+L 13
+U 13
+L 12
+R 10
+U 16
+L 10
+R 14
+D 17
+R 4
+D 16
+U 17
+R 8
+D 10
+R 11
+L 10
+U 17
+L 11
+U 3
+L 4
+U 2
+D 10
+R 15
+U 1
+R 5
+L 12
+D 4
+R 1
+U 15
+R 13
+L 17
+R 15
+D 4
+L 14
+R 3
+D 1
+U 14
+R 11
+D 12
+L 7
+D 11
+R 12
+U 1
+R 5
+L 11
+D 9
+U 8
+R 1
+U 4
+L 15
+R 12
+U 17
+R 4
+U 14
+L 4
+D 16
+R 9
+U 9
+D 11
+L 7
+D 16
+R 10
+L 10
+R 15
+U 10
+R 6
+L 4
+R 6
+L 12
+U 9
+R 15
+D 12
+L 14
+U 14
+D 6
+U 4
+R 17
+U 2
+L 5
+R 13
+D 15
+U 13
+L 5
+D 3
+L 7
+U 12
+R 13
+D 15
+U 15
+L 17
+U 17
+L 6
+R 2
+U 17
+R 7
+L 15
+U 17
+R 11
+L 2
+U 1
+D 4
+U 2
+R 10
+L 9
+D 18
+U 5
+D 7
+R 18
+L 11
+D 2
+U 13
+D 12
+R 10
+D 2
+R 9
+L 3
+D 9
+R 9
+L 17
+R 4
+L 7
+D 16
+L 5
+R 3
+L 8
+U 7
+R 8
+L 1
+D 12
+U 13
+R 8
+D 18
+U 7
+R 6
+D 7
+L 7
+D 8
+R 16
+L 13
+R 6
+U 1
+L 9
+U 9
+L 12
+D 13
+R 18
+L 7
+D 18
+U 17
+R 18
+D 11
+R 1
+D 4
+U 3
+L 7
+D 17
+R 8
+U 11
+D 7
+L 4
+R 12
+U 10
+L 15
+R 14
+U 16
+D 4
+L 10
+U 6
+D 18
+R 7
+U 13
+R 11
+D 14
+L 5
+U 5
+L 7
+R 15
+L 16
+U 6
+D 9
+L 17
+R 4
+L 16
+D 10
+U 16
+D 17
+R 7
+L 16
+U 6
+L 15
+R 13
+U 8
+L 14
+R 12
+U 5
+D 17
+R 13
+D 12
+U 12
+L 16
+R 8
+U 4
+D 6
+U 10
+D 14
+R 18
+L 17
+U 15
+L 5
+U 7
+D 15
+U 10
+R 18
+L 11
+D 10
+R 12
+U 15
+D 11
+U 7
+D 3
+R 2
+U 5
+R 12
+U 15
+R 12
+U 19
+L 6
+U 18
+D 8
+L 7
+D 2
+R 1
+L 16
+D 5
+L 9
+U 11
+L 6
+U 7
+L 2
+D 18
+L 1
+U 19
+D 15
+U 8
+R 5
+U 13
+L 7
+R 14
+U 2
+D 10
+U 16
+D 3
+R 8
+L 10
+D 17
+U 16
+D 6
+R 18
+D 5
+L 5
+D 4
+L 6
+U 17
+D 7
+R 15
+D 5
+R 18
+L 6
+U 15
+L 3
+U 13
+L 15
+R 5
+D 9
+R 11
+U 15
+L 19
+D 11
+L 8
+D 14
+R 7
+L 16
+D 5
+R 13
+D 5
+L 9
+D 3
+L 3
+R 12
+U 17
+R 8
+L 11
+U 2
+R 9
+L 3
+D 6
+L 9
+U 11
+R 6
+U 19
+R 6
+L 12
+U 5
+R 4
+U 4
+L 16
+R 17
+D 12
+U 11
+R 4
+D 11
+U 4
+R 8
+U 9
+R 12
+U 13
+L 2
+D 9
+L 2
+
diff --git a/day09/part1.rs b/day09/part1.rs
new file mode 100644
index 0000000..ec169bf
--- /dev/null
+++ b/day09/part1.rs
@@ -0,0 +1,27 @@
+// 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 std::collections::HashSet;
+
+fn main() -> Result<(), &'static str> {
+ let tail_positions: Result, _> =
+ common::rope_states().map(|s| Ok(s?.tail_pos())).collect();
+ println!("{}", tail_positions?.len());
+ Ok(())
+}
diff --git a/day09/testinput b/day09/testinput
new file mode 100644
index 0000000..9874df2
--- /dev/null
+++ b/day09/testinput
@@ -0,0 +1,8 @@
+R 4
+U 4
+L 3
+D 1
+R 4
+D 1
+L 5
+R 2