diff --git a/src/bin/d22p1.rs b/src/bin/d22p1.rs
index b3a16af..7150c8c 100644
--- a/src/bin/d22p1.rs
+++ b/src/bin/d22p1.rs
@@ -9,7 +9,7 @@ pub fn main() {
     // dbg!(&grid);
     // dbg!(&instructions);
 
-    let mut pose = day22::Pose::new(&grid);
+    let mut pose = grid.initial_pose();
     // dbg!(&pose);
     for inst in &instructions {
         pose = grid.exec_instruction(&pose, inst).0;
diff --git a/src/bin/d22p2.rs b/src/bin/d22p2.rs
new file mode 100644
index 0000000..a4ca300
--- /dev/null
+++ b/src/bin/d22p2.rs
@@ -0,0 +1,40 @@
+use std::collections::HashMap;
+
+use aoc22::{
+    day22::{self, CubeSide, Navigable},
+    util,
+};
+
+pub fn main() {
+    let (grid, instructions) = day22::parse_map_and_path(&util::parse_input());
+
+    let mut pattern = HashMap::new();
+    pattern.insert(CubeSide::Top, (0, 1));
+    pattern.insert(CubeSide::Fore, (1, 1));
+    pattern.insert(CubeSide::Left, (2, 0));
+    pattern.insert(CubeSide::Back, (3, 0));
+    pattern.insert(CubeSide::Bottom, (2, 1));
+    pattern.insert(CubeSide::Right, (0, 2));
+
+    // Pattern for example input
+    // WARNING: CubeGrid::wrap_around is hardcoded for the pattern above
+    // pattern.insert(CubeSide::Top, (0, 2));
+    // pattern.insert(CubeSide::Fore, (1, 2));
+    // pattern.insert(CubeSide::Left, (1, 1));
+    // pattern.insert(CubeSide::Back, (1, 0));
+    // pattern.insert(CubeSide::Bottom, (2, 2));
+    // pattern.insert(CubeSide::Right, (2, 3));
+
+    let grid = day22::CubeGrid::from(&grid, &pattern);
+    let mut pose = grid.initial_pose();
+    for inst in &instructions {
+        pose = grid.exec_instruction(&pose, inst).0;
+    }
+    let pos = pose.1.pos;
+    let row_add = pattern[&pose.0].0 * grid.side_height;
+    let col_add = pattern[&pose.0].1 * grid.side_width;
+
+    let pass =
+        1000 * (row_add + pos.0 + 1) + 4 * (col_add + pos.1 + 1) + (pose.1.orientation as usize);
+    println!("Passowrd: {}", pass);
+}
diff --git a/src/day22.rs b/src/day22.rs
index e51f701..7514f15 100644
--- a/src/day22.rs
+++ b/src/day22.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
 use itertools::Itertools;
 
 use crate::util::Dir;
@@ -20,19 +22,7 @@ pub struct Pose {
     pub orientation: Dir,
 }
 
-impl Pose {
-    pub fn new(grid: &SparseGrid) -> Pose {
-        let col = grid.grid[0]
-            .iter()
-            .position(|t| if let Some(occ) = t { !occ } else { false })
-            .unwrap();
-        Pose {
-            pos: (0, col),
-            orientation: Dir::Right,
-        }
-    }
-}
-
+#[derive(Debug)]
 pub enum MoveResult {
     Blocked,
     BlockedByWrapping,
@@ -63,6 +53,7 @@ pub trait Navigable {
         from: &Self::Pose,
         inst: &Self::NavigationInstruction,
     ) -> (Self::Pose, MoveResult);
+    fn initial_pose(&self) -> Self::Pose;
 }
 
 #[derive(Debug)]
@@ -74,6 +65,40 @@ pub struct SparseGrid {
     pub col_bounds: Vec<(usize, usize)>,
 }
 
+impl SparseGrid {
+    fn print(&self, pose: Option<&Pose>) {
+        let check_pos = |y, x| {
+            if let Some(pose) = pose {
+                pose.pos.0 == y && pose.pos.1 == x
+            } else {
+                false
+            }
+        };
+
+        for (y, row) in self.grid.iter().enumerate() {
+            for (x, col) in row.iter().enumerate() {
+                let c = if check_pos(y, x) {
+                    assert!(*col == Some(false));
+                    match pose.unwrap().orientation {
+                        Dir::Right => '>',
+                        Dir::Down => 'v',
+                        Dir::Left => '<',
+                        Dir::Up => '^',
+                    }
+                } else if col.is_none() {
+                    ' '
+                } else if *col == Some(true) {
+                    '#'
+                } else {
+                    '.'
+                };
+                print!("{}", c);
+            }
+            print!("\n");
+        }
+    }
+}
+
 impl Navigable for SparseGrid {
     type Pose = Pose;
     type NavigationInstruction = PassInstr;
@@ -159,6 +184,352 @@ impl Navigable for SparseGrid {
             ),
         }
     }
+
+    fn initial_pose(&self) -> Self::Pose {
+        let col = self.grid[0]
+            .iter()
+            .position(|t| if let Some(occ) = t { !occ } else { false })
+            .unwrap();
+        Pose {
+            pos: (0, col),
+            orientation: Dir::Right,
+        }
+    }
+}
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
+pub enum CubeSide {
+    Top,
+    Bottom,
+    Left,
+    Right,
+    Fore,
+    Back,
+}
+
+#[derive(Debug)]
+pub struct CubeGrid {
+    // The side grids don't have to be sparse, but we don't have a dense
+    // implementation...
+    pub sides: HashMap<CubeSide, SparseGrid>,
+
+    pub side_height: usize,
+    pub side_width: usize,
+}
+
+impl CubeGrid {
+    pub fn from(sparse: &SparseGrid, pattern: &HashMap<CubeSide, (usize, usize)>) -> CubeGrid {
+        let fold_width = pattern.values().map(|(_, col)| col).max().unwrap() + 1;
+        let fold_height = pattern.values().map(|(row, _)| row).max().unwrap() + 1;
+        let side_width = sparse.width / fold_width;
+        assert!(sparse.width % fold_width == 0);
+        let side_height = sparse.height / fold_height;
+        assert!(sparse.height % fold_height == 0);
+
+        let mut sides = HashMap::new();
+        for (side, (fold_row, fold_col)) in pattern {
+            let mut grid = Vec::new();
+            for r in 0..side_height {
+                let mut row = Vec::new();
+                for c in 0..side_width {
+                    let tile = sparse.grid[fold_row * side_height + r][fold_col * side_width + c];
+                    assert!(tile.is_some());
+                    row.push(tile);
+                }
+                grid.push(row);
+            }
+            let grid = SparseGrid {
+                grid,
+                height: side_height,
+                width: side_width,
+                row_bounds: vec![(0, side_width - 1); side_height],
+                col_bounds: vec![(0, side_height - 1); side_width],
+            };
+            assert!(sides.insert(*side, grid).is_none());
+        }
+
+        CubeGrid {
+            sides,
+            side_height,
+            side_width,
+        }
+    }
+
+    pub fn print(&self, pose: &(CubeSide, Pose)) {
+        for (side, grid) in &self.sides {
+            if pose.0 != *side {
+                continue;
+            }
+            println!("{:?}:", side);
+            grid.print(Some(&pose.1));
+            println!("");
+        }
+    }
+
+    fn wrap_around(&self, side: CubeSide, pose: &Pose) -> (CubeSide, Pose) {
+        // FIXME: This is hardcoded for the folding pattern of the actual input.
+        // It won't work for the example input.
+        assert_eq!(self.side_height, self.side_width);
+        let dir = pose.orientation;
+        let pos = pose.pos;
+        let row = pos.0;
+        let last = self.side_height - 1;
+        let row_inv = last - row;
+        let col = pos.1;
+        match side {
+            CubeSide::Top => match dir {
+                Dir::Right => (
+                    CubeSide::Right,
+                    Pose {
+                        pos: (row, 0),
+                        orientation: Dir::Right,
+                    },
+                ),
+                Dir::Down => (
+                    CubeSide::Fore,
+                    Pose {
+                        pos: (0, col),
+                        orientation: Dir::Down,
+                    },
+                ),
+                Dir::Left => (
+                    CubeSide::Left,
+                    Pose {
+                        pos: (row_inv, 0),
+                        orientation: Dir::Right,
+                    },
+                ),
+                Dir::Up => (
+                    CubeSide::Back,
+                    Pose {
+                        pos: (col, 0),
+                        orientation: Dir::Right,
+                    },
+                ),
+            },
+            CubeSide::Bottom => match dir {
+                Dir::Right => (
+                    CubeSide::Right,
+                    Pose {
+                        pos: (row_inv, last),
+                        orientation: Dir::Left,
+                    },
+                ),
+                Dir::Down => (
+                    CubeSide::Back,
+                    Pose {
+                        pos: (col, last),
+                        orientation: Dir::Left,
+                    },
+                ),
+                Dir::Left => (
+                    CubeSide::Left,
+                    Pose {
+                        pos: (row, last),
+                        orientation: Dir::Left,
+                    },
+                ),
+                Dir::Up => (
+                    CubeSide::Fore,
+                    Pose {
+                        pos: (last, col),
+                        orientation: Dir::Up,
+                    },
+                ),
+            },
+            CubeSide::Left => match dir {
+                Dir::Right => (
+                    CubeSide::Bottom,
+                    Pose {
+                        pos: (row, 0),
+                        orientation: Dir::Right,
+                    },
+                ),
+                Dir::Down => (
+                    CubeSide::Back,
+                    Pose {
+                        pos: (0, col),
+                        orientation: Dir::Down,
+                    },
+                ),
+                Dir::Left => (
+                    CubeSide::Top,
+                    Pose {
+                        pos: (row_inv, 0),
+                        orientation: Dir::Right,
+                    },
+                ),
+                Dir::Up => (
+                    CubeSide::Fore,
+                    Pose {
+                        pos: (col, 0),
+                        orientation: Dir::Right,
+                    },
+                ),
+            },
+            CubeSide::Right => match dir {
+                Dir::Right => (
+                    CubeSide::Bottom,
+                    Pose {
+                        pos: (row_inv, last),
+                        orientation: Dir::Left,
+                    },
+                ),
+                Dir::Down => (
+                    CubeSide::Fore,
+                    Pose {
+                        pos: (col, last),
+                        orientation: Dir::Left,
+                    },
+                ),
+                Dir::Left => (
+                    CubeSide::Top,
+                    Pose {
+                        pos: (row, last),
+                        orientation: Dir::Left,
+                    },
+                ),
+                Dir::Up => (
+                    CubeSide::Back,
+                    Pose {
+                        pos: (last, col),
+                        orientation: Dir::Up,
+                    },
+                ),
+            },
+            CubeSide::Fore => match dir {
+                Dir::Right => (
+                    CubeSide::Right,
+                    Pose {
+                        pos: (last, row),
+                        orientation: Dir::Up,
+                    },
+                ),
+                Dir::Down => (
+                    CubeSide::Bottom,
+                    Pose {
+                        pos: (0, col),
+                        orientation: Dir::Down,
+                    },
+                ),
+                Dir::Left => (
+                    CubeSide::Left,
+                    Pose {
+                        pos: (0, row),
+                        orientation: Dir::Down,
+                    },
+                ),
+                Dir::Up => (
+                    CubeSide::Top,
+                    Pose {
+                        pos: (last, col),
+                        orientation: Dir::Up,
+                    },
+                ),
+            },
+            CubeSide::Back => match dir {
+                Dir::Right => (
+                    CubeSide::Bottom,
+                    Pose {
+                        pos: (last, row),
+                        orientation: Dir::Up,
+                    },
+                ),
+                Dir::Down => (
+                    CubeSide::Right,
+                    Pose {
+                        pos: (0, col),
+                        orientation: Dir::Down,
+                    },
+                ),
+                Dir::Left => (
+                    CubeSide::Top,
+                    Pose {
+                        pos: (0, row),
+                        orientation: Dir::Down,
+                    },
+                ),
+                Dir::Up => (
+                    CubeSide::Left,
+                    Pose {
+                        pos: (last, col),
+                        orientation: Dir::Up,
+                    },
+                ),
+            },
+        }
+    }
+}
+
+impl Navigable for CubeGrid {
+    type Pose = (CubeSide, Pose);
+    type NavigationInstruction = PassInstr;
+
+    fn forward(
+        &self,
+        (from_side, from_pose): &Self::Pose,
+        steps: usize,
+    ) -> (Self::Pose, MoveResult) {
+        let mut side = from_side.clone();
+        let mut pose = from_pose.clone();
+        let mut status = MoveResult::Success;
+        for _ in 0..steps {
+            let (next_pose, res) = self.sides[&side].forward(&pose, 1);
+            if res.wrapped_around() {
+                let (next_side, next_pose) = self.wrap_around(side, &pose);
+                if self.sides[&next_side].grid[next_pose.pos.0][next_pose.pos.1] == Some(true) {
+                    // Can't wrap around, we're blocked
+                    return ((side, pose), MoveResult::BlockedByWrapping);
+                }
+                side = next_side;
+                pose = next_pose;
+                status = MoveResult::WrappedAround;
+            } else if res.was_blocked() {
+                return ((side, pose), MoveResult::Blocked);
+            } else {
+                pose = next_pose;
+            }
+        }
+
+        ((side, pose), status)
+    }
+
+    fn exec_instruction(
+        &self,
+        from: &Self::Pose,
+        inst: &Self::NavigationInstruction,
+    ) -> (Self::Pose, MoveResult) {
+        match inst {
+            PassInstr::Move(n) => self.forward(from, *n),
+            PassInstr::Turn(dir) => (
+                (
+                    from.0,
+                    Pose {
+                        pos: from.1.pos,
+                        orientation: match dir {
+                            TurnDir::CW => from.1.orientation.cw(),
+                            TurnDir::CCW => from.1.orientation.ccw(),
+                        },
+                    },
+                ),
+                MoveResult::Success,
+            ),
+        }
+    }
+
+    fn initial_pose(&self) -> Self::Pose {
+        let col = self.sides[&CubeSide::Top].grid[0]
+            .iter()
+            .position(|t| *t == Some(false))
+            .unwrap();
+        (
+            CubeSide::Top,
+            Pose {
+                pos: (0, col),
+                orientation: Dir::Right,
+            },
+        )
+    }
 }
 
 pub fn parse_map_and_path(input: &str) -> (SparseGrid, Vec<PassInstr>) {