diff options
| -rw-r--r-- | 2022/22/a.rs | 81 | ||||
| -rw-r--r-- | 2022/22/b.rs | 314 | ||||
| -rw-r--r-- | 2022/22/common.rs | 111 |
3 files changed, 506 insertions, 0 deletions
diff --git a/2022/22/a.rs b/2022/22/a.rs new file mode 100644 index 0000000..40f9c44 --- /dev/null +++ b/2022/22/a.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | use std::cmp::max; | ||
| 2 | mod common; | ||
| 3 | use common::*; | ||
| 4 | |||
| 5 | #[derive(Copy, Clone, Debug)] | ||
| 6 | pub struct Position { | ||
| 7 | pub i: usize, | ||
| 8 | pub j: usize, | ||
| 9 | pub d: Direction | ||
| 10 | } | ||
| 11 | |||
| 12 | pub struct Map { | ||
| 13 | pub tiles: Vec<Vec<Tile>> | ||
| 14 | } | ||
| 15 | |||
| 16 | type StepFn = fn(&Map, Position) -> Position; | ||
| 17 | |||
| 18 | impl Map { | ||
| 19 | pub fn from_stdin() -> Map { | ||
| 20 | let mut maxlen = 0; | ||
| 21 | let mut tiles = vec![]; | ||
| 22 | let mut line = String::new(); | ||
| 23 | while std::io::stdin().read_line(&mut line).unwrap() > 1 { | ||
| 24 | let v = line[0..line.len()-1].as_bytes().iter() | ||
| 25 | .map(|b| Tile::from_byte(*b)).collect::<Vec<_>>(); | ||
| 26 | maxlen = max(maxlen, v.len()); | ||
| 27 | tiles.push(v); | ||
| 28 | line.clear(); | ||
| 29 | } | ||
| 30 | for v in &mut tiles { | ||
| 31 | while v.len() < maxlen { v.push(Tile::Skip); } | ||
| 32 | } | ||
| 33 | Map { tiles } | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn start_position(&self) -> Position { | ||
| 37 | let first_valid = self.tiles[0].iter() | ||
| 38 | .position(|t| *t == Tile::Walk).unwrap(); | ||
| 39 | Position { i: 0, j: first_valid, d: Direction::Right } | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn walk(&self, pos: Position, n: usize, step: StepFn) -> Position { | ||
| 43 | let mut p = pos; | ||
| 44 | for _ in 0..n { p = step(self, p); } | ||
| 45 | p | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | fn step(map: &Map, position: Position) -> Position { | ||
| 50 | let imax = map.tiles.len(); | ||
| 51 | let jmax = map.tiles[0].len(); | ||
| 52 | let s = position.d.step(); | ||
| 53 | let mut p = position; | ||
| 54 | loop { | ||
| 55 | let inext = ((s.0 + imax as i64) as usize + p.i) % imax; | ||
| 56 | let jnext = ((s.1 + jmax as i64) as usize + p.j) % jmax; | ||
| 57 | p = Position { i: inext, j: jnext, d: p.d }; | ||
| 58 | match map.tiles[p.i][p.j] { | ||
| 59 | Tile::Walk => return p, | ||
| 60 | Tile::Wall => return position, | ||
| 61 | Tile::Skip => () | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | fn password(p: Position) -> usize { | ||
| 67 | 1000 * (p.i+1) + 4 * (p.j+1) + p.d.value() | ||
| 68 | } | ||
| 69 | |||
| 70 | fn main() { | ||
| 71 | let map = Map::from_stdin(); | ||
| 72 | let (turns, steps) = read_instruction_line_from_stdin(); | ||
| 73 | let mut position = map.start_position(); | ||
| 74 | |||
| 75 | for i in 0..turns.len() { | ||
| 76 | position = map.walk(position, steps[i], step); | ||
| 77 | position.d = position.d.turn(turns[i]); | ||
| 78 | } | ||
| 79 | position = map.walk(position, steps[steps.len()-1], step); | ||
| 80 | println!("{}", password(position)); | ||
| 81 | } | ||
diff --git a/2022/22/b.rs b/2022/22/b.rs new file mode 100644 index 0000000..1d292ee --- /dev/null +++ b/2022/22/b.rs | |||
| @@ -0,0 +1,314 @@ | |||
| 1 | use std::mem::swap; | ||
| 2 | mod common; | ||
| 3 | use common::*; | ||
| 4 | |||
| 5 | // Use 4 for the test input, 50 for the real one | ||
| 6 | //const CUBE_SIZE: usize = 4; | ||
| 7 | const CUBE_SIZE: usize = 50; | ||
| 8 | |||
| 9 | // Connections between faces to make the cube 3D. | ||
| 10 | // This is hard coded to work for my specific input. | ||
| 11 | // To make it work for a different input you have to change this. | ||
| 12 | // The faces are in top-to-bottom, left-to-right order of input. | ||
| 13 | // For each face, the 4 connections are in order right, down, left, up. | ||
| 14 | |||
| 15 | // Test case. Configuration: | ||
| 16 | // 0 U = 0, D = 4 | ||
| 17 | // 123 R = 5, L = 2 | ||
| 18 | // 45 F = 3, B = 1 | ||
| 19 | /* | ||
| 20 | const CONNECTIONS: [[Connection; 4]; 6] = [ | ||
| 21 | // Face 0 = U | ||
| 22 | [ | ||
| 23 | Connection { face: 5, side: Direction::Right }, | ||
| 24 | Connection { face: 3, side: Direction::Up }, | ||
| 25 | Connection { face: 2, side: Direction::Up }, | ||
| 26 | Connection { face: 1, side: Direction::Up }, | ||
| 27 | ], | ||
| 28 | |||
| 29 | // Face 1 | ||
| 30 | [ | ||
| 31 | Connection { face: 2, side: Direction::Left }, | ||
| 32 | Connection { face: 4, side: Direction::Down }, | ||
| 33 | Connection { face: 5, side: Direction::Down }, | ||
| 34 | Connection { face: 0, side: Direction::Up }, | ||
| 35 | ], | ||
| 36 | |||
| 37 | // Face 2 | ||
| 38 | [ | ||
| 39 | Connection { face: 3, side: Direction::Left }, | ||
| 40 | Connection { face: 4, side: Direction::Left }, | ||
| 41 | Connection { face: 1, side: Direction::Right }, | ||
| 42 | Connection { face: 0, side: Direction::Left }, | ||
| 43 | ], | ||
| 44 | |||
| 45 | // Face 3 | ||
| 46 | [ | ||
| 47 | Connection { face: 5, side: Direction::Up }, | ||
| 48 | Connection { face: 4, side: Direction::Up }, | ||
| 49 | Connection { face: 2, side: Direction::Right }, | ||
| 50 | Connection { face: 0, side: Direction::Down }, | ||
| 51 | ], | ||
| 52 | |||
| 53 | // Face 4 | ||
| 54 | [ | ||
| 55 | Connection { face: 5, side: Direction::Left }, | ||
| 56 | Connection { face: 1, side: Direction::Down }, | ||
| 57 | Connection { face: 2, side: Direction::Down }, | ||
| 58 | Connection { face: 3, side: Direction::Down }, | ||
| 59 | ], | ||
| 60 | |||
| 61 | // Face 5 | ||
| 62 | [ | ||
| 63 | Connection { face: 0, side: Direction::Right }, | ||
| 64 | Connection { face: 1, side: Direction::Left }, | ||
| 65 | Connection { face: 4, side: Direction::Right }, | ||
| 66 | Connection { face: 3, side: Direction::Right }, | ||
| 67 | ], | ||
| 68 | ]; | ||
| 69 | */ | ||
| 70 | |||
| 71 | // Input. Configuration: | ||
| 72 | // 01 | ||
| 73 | // 2 | ||
| 74 | // 34 | ||
| 75 | // 5 | ||
| 76 | const CONNECTIONS: [[Connection; 4]; 6] = [ | ||
| 77 | // Face 0 = U | ||
| 78 | [ | ||
| 79 | Connection { face: 1, side: Direction::Left }, | ||
| 80 | Connection { face: 2, side: Direction::Up }, | ||
| 81 | Connection { face: 3, side: Direction::Left }, | ||
| 82 | Connection { face: 5, side: Direction::Left }, | ||
| 83 | ], | ||
| 84 | |||
| 85 | // Face 1 | ||
| 86 | [ | ||
| 87 | Connection { face: 4, side: Direction::Right }, | ||
| 88 | Connection { face: 2, side: Direction::Right }, | ||
| 89 | Connection { face: 0, side: Direction::Right }, | ||
| 90 | Connection { face: 5, side: Direction::Down }, | ||
| 91 | ], | ||
| 92 | |||
| 93 | // Face 2 | ||
| 94 | [ | ||
| 95 | Connection { face: 1, side: Direction::Down }, | ||
| 96 | Connection { face: 4, side: Direction::Up }, | ||
| 97 | Connection { face: 3, side: Direction::Up }, | ||
| 98 | Connection { face: 0, side: Direction::Down }, | ||
| 99 | ], | ||
| 100 | |||
| 101 | // Face 3 | ||
| 102 | [ | ||
| 103 | Connection { face: 4, side: Direction::Left }, | ||
| 104 | Connection { face: 5, side: Direction::Up }, | ||
| 105 | Connection { face: 0, side: Direction::Left }, | ||
| 106 | Connection { face: 2, side: Direction::Left }, | ||
| 107 | ], | ||
| 108 | |||
| 109 | // Face 4 | ||
| 110 | [ | ||
| 111 | Connection { face: 1, side: Direction::Right }, | ||
| 112 | Connection { face: 5, side: Direction::Right }, | ||
| 113 | Connection { face: 3, side: Direction::Right }, | ||
| 114 | Connection { face: 2, side: Direction::Down }, | ||
| 115 | ], | ||
| 116 | |||
| 117 | // Face 5 | ||
| 118 | [ | ||
| 119 | Connection { face: 4, side: Direction::Down }, | ||
| 120 | Connection { face: 1, side: Direction::Up }, | ||
| 121 | Connection { face: 0, side: Direction::Up }, | ||
| 122 | Connection { face: 3, side: Direction::Down }, | ||
| 123 | ], | ||
| 124 | ]; | ||
| 125 | |||
| 126 | |||
| 127 | #[derive(Copy, Clone, Debug)] | ||
| 128 | struct Connection { | ||
| 129 | pub face: usize, | ||
| 130 | pub side: Direction | ||
| 131 | } | ||
| 132 | |||
| 133 | struct Face { | ||
| 134 | pub tiles: [[Tile; CUBE_SIZE]; CUBE_SIZE], | ||
| 135 | offset_i: usize, | ||
| 136 | offset_j: usize | ||
| 137 | } | ||
| 138 | |||
| 139 | #[derive(PartialEq, Debug, Copy, Clone)] | ||
| 140 | struct Position { | ||
| 141 | f: usize, | ||
| 142 | i: usize, | ||
| 143 | j: usize, | ||
| 144 | d: Direction | ||
| 145 | } | ||
| 146 | |||
| 147 | #[allow(dead_code)] | ||
| 148 | impl Face { | ||
| 149 | pub fn empty() -> Face { | ||
| 150 | Face { | ||
| 151 | tiles: [[Tile::Skip; CUBE_SIZE]; CUBE_SIZE], | ||
| 152 | offset_i: 0, | ||
| 153 | offset_j: 0 | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | pub fn is_empty(&self) -> bool { | ||
| 158 | self.tiles[0][0] == Tile::Skip | ||
| 159 | } | ||
| 160 | |||
| 161 | pub fn print(&self) { | ||
| 162 | for i in 0..CUBE_SIZE { | ||
| 163 | for j in 0..CUBE_SIZE { | ||
| 164 | print!("{}", self.tiles[i][j].to_char()); | ||
| 165 | } | ||
| 166 | println!(); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | struct Cube { | ||
| 172 | pub faces: Vec<Face> | ||
| 173 | } | ||
| 174 | |||
| 175 | type StepFn = fn(&Cube, Position) -> Position; | ||
| 176 | |||
| 177 | impl Cube { | ||
| 178 | pub fn from_stdin() -> Cube { | ||
| 179 | let mut faces = vec![]; | ||
| 180 | let mut line = String::new(); | ||
| 181 | let mut offset_i = 0; | ||
| 182 | while faces.len() < 6 { | ||
| 183 | let mut row = [Face::empty(), Face::empty(), Face::empty(), | ||
| 184 | Face::empty(), Face::empty(), Face::empty()]; | ||
| 185 | for k in 0..CUBE_SIZE { | ||
| 186 | let _ = std::io::stdin().read_line(&mut line); | ||
| 187 | let bytes = &line.as_bytes(); | ||
| 188 | for l in 0..line.len()-1 { | ||
| 189 | row[l / CUBE_SIZE].tiles[k][l % CUBE_SIZE] = | ||
| 190 | Tile::from_byte(bytes[l]); | ||
| 191 | } | ||
| 192 | line.clear(); | ||
| 193 | } | ||
| 194 | let mut offset_j = 0; | ||
| 195 | for mut r in row { | ||
| 196 | r.offset_i = offset_i; | ||
| 197 | r.offset_j = offset_j; | ||
| 198 | if !r.is_empty() { | ||
| 199 | faces.push(r); | ||
| 200 | } | ||
| 201 | offset_j += CUBE_SIZE; | ||
| 202 | } | ||
| 203 | offset_i += CUBE_SIZE; | ||
| 204 | } | ||
| 205 | let _ = std::io::stdin().read_line(&mut line); // Read empty line | ||
| 206 | assert!(faces.len() == 6, "Cube has {} faces", faces.len()); | ||
| 207 | Cube { faces } | ||
| 208 | } | ||
| 209 | |||
| 210 | fn start_position(&self) -> Position { | ||
| 211 | let j = self.faces[0].tiles[0].iter() | ||
| 212 | .position(|t| *t == Tile::Walk).unwrap(); | ||
| 213 | Position { f: 0, i: 0, j, d: Direction::Right } | ||
| 214 | } | ||
| 215 | |||
| 216 | pub fn walk(&self, pos: Position, n: usize, step: StepFn) -> Position { | ||
| 217 | let mut p = pos; | ||
| 218 | for _ in 0..n { p = step(self, p); } | ||
| 219 | p | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | fn flips(d1: Direction, d2: Direction) -> bool { | ||
| 224 | match d1 { | ||
| 225 | Direction::Right | Direction::Up => | ||
| 226 | d2 == Direction::Right || d2 == Direction::Up, | ||
| 227 | Direction::Down | Direction::Left => | ||
| 228 | d2 == Direction::Down || d2 == Direction::Left | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | fn swaps(d1: Direction, d2: Direction) -> bool { | ||
| 233 | match d1 { | ||
| 234 | Direction::Right | Direction::Left => | ||
| 235 | d2 == Direction::Up || d2 == Direction::Down, | ||
| 236 | Direction::Up | Direction::Down => | ||
| 237 | d2 == Direction::Right || d2 == Direction::Left | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | fn validate_connections(conn: &[[Connection; 4]; 6]) { | ||
| 242 | for i in 0..6 { | ||
| 243 | for j in 0..4 { | ||
| 244 | let c = conn[i][j]; | ||
| 245 | let d = conn[c.face][c.side.value()]; | ||
| 246 | assert!(d.face == i, "Bad connection: face {}-{}", i, j); | ||
| 247 | assert!(d.side.value() == j, "Bad connection: side {}-{}", i, j); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | fn overflow(i: i64, j: i64) -> Option<Direction> { | ||
| 253 | if i == -1 { return Some(Direction::Up) } | ||
| 254 | if j == -1 { return Some(Direction::Left) } | ||
| 255 | if i as usize == CUBE_SIZE { return Some(Direction::Down) } | ||
| 256 | if j as usize == CUBE_SIZE { return Some(Direction::Right) } | ||
| 257 | None | ||
| 258 | } | ||
| 259 | |||
| 260 | fn on_other_side(i: i64, flip: bool, positive: bool) -> usize { | ||
| 261 | const CUBE_SIZE_I64: i64 = CUBE_SIZE as i64; | ||
| 262 | (if i >= 0 && i < CUBE_SIZE_I64 { | ||
| 263 | if flip { CUBE_SIZE_I64 - i - 1 } else { i } | ||
| 264 | } else { | ||
| 265 | if positive { 0 } else { CUBE_SIZE_I64 - 1 } | ||
| 266 | }) as usize | ||
| 267 | } | ||
| 268 | |||
| 269 | fn step(cube: &Cube, p: Position) -> Position { | ||
| 270 | let s = p.d.step(); | ||
| 271 | let mut inext = s.0 + p.i as i64; | ||
| 272 | let mut jnext = s.1 + p.j as i64; | ||
| 273 | |||
| 274 | let ret = if let Some(d) = overflow(inext, jnext) { | ||
| 275 | let conn = CONNECTIONS[p.f][d.value()]; | ||
| 276 | let flip = flips(p.d, conn.side); | ||
| 277 | let new_direction = conn.side.opposite(); | ||
| 278 | let positive = new_direction.is_positive(); | ||
| 279 | if swaps(p.d, conn.side) { swap(&mut inext, &mut jnext) } | ||
| 280 | Position { | ||
| 281 | f: conn.face, | ||
| 282 | i: on_other_side(inext, flip, positive), | ||
| 283 | j: on_other_side(jnext, flip, positive), | ||
| 284 | d: conn.side.opposite() | ||
| 285 | } | ||
| 286 | } else { | ||
| 287 | Position { | ||
| 288 | f: p.f, | ||
| 289 | i: inext as usize, | ||
| 290 | j: jnext as usize, | ||
| 291 | d: p.d | ||
| 292 | } | ||
| 293 | }; | ||
| 294 | |||
| 295 | if cube.faces[ret.f].tiles[ret.i][ret.j] == Tile::Wall { p } else { ret } | ||
| 296 | } | ||
| 297 | |||
| 298 | fn password(cube: &Cube, p: Position) -> usize { | ||
| 299 | let f = &cube.faces[p.f]; | ||
| 300 | 1000 * (p.i + f.offset_i + 1) + 4 * (p.j + f.offset_j + 1) + p.d.value() | ||
| 301 | } | ||
| 302 | |||
| 303 | fn main() { | ||
| 304 | validate_connections(&CONNECTIONS); | ||
| 305 | let cube = Cube::from_stdin(); | ||
| 306 | let mut position = cube.start_position(); | ||
| 307 | let (turns, steps) = read_instruction_line_from_stdin(); | ||
| 308 | for i in 0..turns.len() { | ||
| 309 | position = cube.walk(position, steps[i], step); | ||
| 310 | position.d = position.d.turn(turns[i]); | ||
| 311 | } | ||
| 312 | position = cube.walk(position, steps[steps.len()-1], step); | ||
| 313 | println!("{}", password(&cube, position)); | ||
| 314 | } | ||
diff --git a/2022/22/common.rs b/2022/22/common.rs new file mode 100644 index 0000000..881513c --- /dev/null +++ b/2022/22/common.rs | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | use std::cmp::*; | ||
| 2 | |||
| 3 | #[derive(PartialEq, Copy, Clone, Debug)] | ||
| 4 | pub enum Tile { Walk, Wall, Skip } | ||
| 5 | |||
| 6 | #[allow(dead_code)] | ||
| 7 | impl Tile { | ||
| 8 | pub fn from_byte(b: u8) -> Tile { | ||
| 9 | match b { | ||
| 10 | b'.' => Tile::Walk, | ||
| 11 | b'#' => Tile::Wall, | ||
| 12 | b' ' => Tile::Skip, | ||
| 13 | _ => panic!("not a valid tile {}", b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | pub fn to_char(&self) -> char { | ||
| 18 | match self { | ||
| 19 | Tile::Walk => '.', | ||
| 20 | Tile::Wall => '#', | ||
| 21 | Tile::Skip => ' ' | ||
| 22 | } | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | #[derive(PartialEq, Copy, Clone, Debug)] | ||
| 27 | pub enum Direction { Up, Down, Right, Left } | ||
| 28 | |||
| 29 | #[allow(dead_code)] | ||
| 30 | impl Direction { | ||
| 31 | pub fn from_char(c: char) -> Direction { | ||
| 32 | match c { | ||
| 33 | 'U' => Direction::Up, | ||
| 34 | 'D' => Direction::Down, | ||
| 35 | 'R' => Direction::Right, | ||
| 36 | 'L' => Direction::Left, | ||
| 37 | _ => panic!("invalid char for direction") | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn turn(&self, t: Direction) -> Direction { | ||
| 42 | match t { | ||
| 43 | Direction::Right => match self { | ||
| 44 | Direction::Up => Direction::Right, | ||
| 45 | Direction::Right => Direction::Down, | ||
| 46 | Direction::Down => Direction::Left, | ||
| 47 | Direction::Left => Direction::Up | ||
| 48 | }, | ||
| 49 | Direction::Left => match self { | ||
| 50 | Direction::Up => Direction::Left, | ||
| 51 | Direction::Left => Direction::Down, | ||
| 52 | Direction::Down => Direction::Right, | ||
| 53 | Direction::Right => Direction::Up | ||
| 54 | } | ||
| 55 | _ => panic!("cannot turn up or down") | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn step(&self) -> (i64, i64) { | ||
| 60 | match self { | ||
| 61 | Direction::Up => (-1, 0), | ||
| 62 | Direction::Down => (1, 0), | ||
| 63 | Direction::Right => (0, 1), | ||
| 64 | Direction::Left => (0, -1) | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn value(&self) -> usize { | ||
| 69 | match self { | ||
| 70 | Direction::Right => 0, | ||
| 71 | Direction::Down => 1, | ||
| 72 | Direction::Left => 2, | ||
| 73 | Direction::Up => 3 | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn opposite(&self) -> Direction { | ||
| 78 | match self { | ||
| 79 | Direction::Right => Direction::Left, | ||
| 80 | Direction::Left => Direction::Right, | ||
| 81 | Direction::Up => Direction::Down, | ||
| 82 | Direction::Down => Direction::Up | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn is_positive(&self) -> bool { | ||
| 87 | *self == Direction::Down || *self == Direction::Right | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | pub fn read_instruction_line_from_stdin() -> (Vec<Direction>, Vec<usize>) { | ||
| 92 | let mut dir = vec![]; | ||
| 93 | let mut step = vec![]; | ||
| 94 | let mut line = String::new(); | ||
| 95 | let _ = std::io::stdin().read_line(&mut line); | ||
| 96 | let mut i = 0; | ||
| 97 | while i < line.len()-1 { | ||
| 98 | match line.chars().nth(i).unwrap() { | ||
| 99 | '0'..='9' => { | ||
| 100 | let j = line[i..].find(|c: char| !c.is_digit(10)).unwrap() + i; | ||
| 101 | step.push(line[i..j].parse::<usize>().unwrap()); | ||
| 102 | i = j; | ||
| 103 | }, | ||
| 104 | c => { | ||
| 105 | dir.push(Direction::from_char(c)); | ||
| 106 | i += 1; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | (dir, step) | ||
| 111 | } | ||
