diff --git a/day09/src/main.rs b/day09/src/main.rs
index b898ef3..b97ad4a 100644
--- a/day09/src/main.rs
+++ b/day09/src/main.rs
@@ -1,6 +1,12 @@
+use std::{
+    collections::HashSet,
+    fmt::{Debug, Display, Write},
+};
+
 fn main() {
     let input = std::fs::read_to_string("input").unwrap();
     println!("{}", pt1(&input));
+    println!("{}", pt2(&input));
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -10,7 +16,6 @@ enum Cell {
 }
 
 fn pt1(input: &str) -> usize {
-    let mut next_id = 0;
     let mut map = Vec::new();
     let mut frees = Vec::new();
     for (i, c) in input.chars().enumerate() {
@@ -21,11 +26,10 @@ fn pt1(input: &str) -> usize {
             }
         };
         if i % 2 == 0 {
-            let cell = Cell::Block(next_id);
+            let cell = Cell::Block(i / 2);
             for _ in 0..n {
                 map.push(cell);
             }
-            next_id += 1;
         } else {
             for _ in 0..n {
                 frees.push(map.len());
@@ -47,6 +51,100 @@ fn pt1(input: &str) -> usize {
         map.swap(free, i);
     }
 
+    map.iter()
+        .enumerate()
+        .map(|(i, &b)| match b {
+            Cell::Block(v) => i * v,
+            _ => 0,
+        })
+        .sum()
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+enum Block {
+    Free(FreeBlock),
+    // size, id
+    File(FileBlock),
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+struct FreeBlock {
+    size: usize,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+struct FileBlock {
+    size: usize,
+    id: usize,
+}
+
+impl Block {
+    fn is_free(&self) -> Option<usize> {
+        match self {
+            Block::Free(s) => Some(s.size),
+            _ => None,
+        }
+    }
+
+    fn size(&self) -> usize {
+        match self {
+            Block::Free(block) => block.size,
+            Block::File(block) => block.size,
+        }
+    }
+
+    fn set_size(&mut self, s: usize) {
+        match self {
+            Block::Free(block) => block.size = s,
+            Block::File(block) => block.size = s,
+        }
+    }
+}
+
+fn pt2(input: &str) -> usize {
+    let mut map = Vec::new();
+
+    for (i, c) in input.chars().enumerate() {
+        let n = match c.to_digit(10) {
+            Some(n) => n as usize,
+            _ => {
+                continue;
+            }
+        };
+        if i % 2 == 0 {
+            let cell = Block::File(FileBlock { size: n, id: i / 2 });
+            map.push(cell);
+        } else {
+            map.push(Block::Free(FreeBlock { size: n }));
+        }
+    }
+
+    let mut processed = HashSet::new();
+    let mut done = false;
+    while !done {
+        for i in (0..map.len()).rev() {
+            if i == 0 {
+                done = true;
+            }
+            match map[i] {
+                Block::Free(_) => {
+                    continue;
+                }
+                Block::File(file) => {
+                    if processed.contains(&file.id) {
+                        continue;
+                    }
+                    processed.insert(file.id);
+                    if move_file(&mut map, i) {
+                        map = compact(&map);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    let map = render_map(&map);
+
     //println!("{map:?}");
 
     map.iter()
@@ -58,14 +156,102 @@ fn pt1(input: &str) -> usize {
         .sum()
 }
 
-enum Block {
-    Free(usize),
-    // size, id
-    Block(usize, usize),
+fn move_file(map: &mut Vec<Block>, file_index: usize) -> bool {
+    let file_size = map[file_index].size();
+    for j in 0..file_index {
+        if let Some(free_size) = map[j].is_free() {
+            if map[file_index].size() <= free_size {
+                map.swap(file_index, j);
+                let diff = free_size - file_size;
+                if diff > 0 {
+                    map[file_index].set_size(file_size);
+                    map.insert(j + 1, Block::Free(FreeBlock { size: diff }));
+                    return true;
+                }
+                break;
+            }
+        }
+    }
+    false
 }
 
-fn pt2(input: &str) -> usize {
-    todo!()
+fn render_map(map: &[Block]) -> Vec<Cell> {
+    let mut out = Vec::new();
+
+    for block in map {
+        match block {
+            Block::Free(free) => {
+                for _ in 0..free.size {
+                    out.push(Cell::Free);
+                }
+            }
+            Block::File(file) => {
+                for _ in 0..file.size {
+                    out.push(Cell::Block(file.id));
+                }
+            }
+        }
+    }
+
+    out
+}
+
+impl Display for Block {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Block::Free(FreeBlock { size: s }) => {
+                let out = (0..*s).fold(String::new(), |mut out, _| {
+                    let _ = write!(out, ".");
+                    out
+                });
+                write!(f, "{out}")
+            }
+            Block::File(FileBlock { size: sz, id }) => {
+                let out = (0..*sz).fold(String::new(), |mut out, _| {
+                    if out.is_empty() {
+                        let _ = write!(out, "{id}");
+                    } else {
+                        let _ = write!(out, "|{id}");
+                    }
+                    out
+                });
+                write!(f, "{out}")
+            }
+        }
+    }
+}
+
+impl Debug for Block {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Block::Free(s) => write!(f, "free: {}", s.size),
+            Block::File(b) => write!(f, "{}: {}", b.id, b.size),
+        }
+    }
+}
+
+fn compact(v: &[Block]) -> Vec<Block> {
+    let mut out = Vec::new();
+    let mut i = 0;
+
+    while i < v.len() {
+        match v[i] {
+            Block::File(file) => out.push(Block::File(file)),
+            Block::Free(free) => {
+                let mut total = free.size;
+                while let Some(next_free_size) = v.get(i + 1).and_then(|b| b.is_free()) {
+                    total += next_free_size;
+                    i += 1;
+                }
+                if total > 0 {
+                    out.push(Block::Free(FreeBlock { size: total }));
+                }
+            }
+        }
+        i += 1;
+    }
+
+    out
 }
 
 #[cfg(test)]
@@ -79,4 +265,9 @@ mod test {
         let v = pt1(INPUT);
         assert_eq!(v, 1928);
     }
+    #[test]
+    fn p2() {
+        let v = pt2(INPUT);
+        assert_eq!(v, 2858);
+    }
 }