aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

b.rs (9136B)


      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 }