aoc

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

common.rs (6774B)


      1 // This code is more complicated than it should be. The reason is that
      2 // for part 1 I decided to ignore the start and finish position, and not
      3 // to treat them as other cells. This way I could get away with not
      4 // considering wall cells '#' at all.
      5 // But then for part 2 it was more convenient to include them, so I
      6 // added an enum Position {} that treats start and finish as special
      7 // cases. This is quite messy.
      8 
      9 use std::collections::{VecDeque, HashSet};
     10 
     11 #[derive(PartialEq, Debug)]
     12 pub enum Direction { Up, Down, Left, Right, None }
     13 
     14 impl Direction {
     15     pub fn from_byte(b: u8) -> Direction {
     16         match b {
     17             b'^' => Direction::Up,
     18             b'v' => Direction::Down,
     19             b'>' => Direction::Right,
     20             b'<' => Direction::Left,
     21             b'.' => Direction::None,
     22             x => panic!("unexpected char '{}'", x as char)
     23         }
     24     }
     25 }
     26 
     27 #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
     28 pub enum Position {
     29     Start,
     30     Finish,
     31     Other { i: usize, j: usize }
     32 }
     33 
     34 impl Position {
     35     pub fn dist(&self, other: Position) -> usize {
     36         match (*self, other) {
     37             (Position::Start, Position::Start) |
     38             (Position::Finish, Position::Finish) =>
     39                 0,
     40             (Position::Start, Position::Finish) |
     41             (Position::Finish, Position::Start) =>
     42                 usize::MAX, // Not technically correct, but it works
     43             (Position::Start | Position::Finish, Position::Other { i, j }) |
     44             (Position::Other { i, j }, Position::Start | Position::Finish) =>
     45                 i + j,
     46             (Position::Other { i: i1, j: j1 },
     47              Position::Other { i: i2, j: j2 }) => {
     48                 let i = (i1 as i64 - i2 as i64).abs() as usize;
     49                 let j = (j1 as i64 - j2 as i64).abs() as usize;
     50                 i + j
     51             }
     52         }
     53     }
     54 }
     55 
     56 pub struct Map {
     57     pub tiles: Vec<Vec<Direction>>,
     58     pub imax: usize,
     59     pub jmax: usize
     60 }
     61 
     62 #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
     63 struct Node {
     64     p: Position,
     65     t: usize
     66 }
     67 
     68 impl Map {
     69     fn submod(i: usize, t: usize, m: usize) -> usize {
     70         let maybeneg = ((i as i64) - (t as i64)) % m as i64;
     71         (maybeneg as usize + m) % m
     72     }
     73 
     74     pub fn is_busy(&self, p: Position, t: usize) -> bool {
     75         match p {
     76             Position::Start | Position::Finish => false,
     77             Position::Other { i, j } => {
     78                 let iup = Self::submod(i, t, self.imax);
     79                 let idown = (i + t) % self.imax;
     80                 let jleft = Map::submod(j, t, self.jmax);
     81                 let jright = (j + t) % self.jmax;
     82 
     83                 self.tiles[iup][j] == Direction::Down ||
     84                 self.tiles[idown][j] == Direction::Up ||
     85                 self.tiles[i][jleft] == Direction::Right ||
     86                 self.tiles[i][jright] == Direction::Left
     87             }
     88         }
     89     }
     90 
     91     #[allow(dead_code)]
     92     pub fn print(&self, t: usize) {
     93         print!("#.");
     94         for _ in 0..self.jmax { print!("#"); }
     95         println!();
     96 
     97         for i in 0..self.imax {
     98             print!("#");
     99             for j in 0..self.jmax {
    100                 let p = Position::Other { i, j };
    101                 print!("{}", if self.is_busy(p, t) { '*' } else { '.' });
    102             }
    103             println!("#");
    104         }
    105 
    106         for _ in 0..self.jmax { print!("#"); }
    107         print!(".#");
    108         println!();
    109     }
    110 
    111     fn mv(&self, p: Position, d: Direction) -> Option<Position> {
    112         if d == Direction::None { return Some(p); }
    113         match p {
    114             Position::Start => if d == Direction::Down {
    115                 Some(Position::Other { i: 0, j: 0 })
    116             } else { None },
    117             Position::Finish => if d == Direction::Up {
    118                 Some(Position::Other { i: self.imax-1, j: self.jmax-1 })
    119             } else { None },
    120             Position::Other { i, j } => match d {
    121                 Direction::Up => if i > 0 {
    122                     Some(Position::Other { i: i-1, j })
    123                 } else {
    124                     if i == 0 && j == 0 {
    125                         Some(Position::Start)
    126                     } else { None }
    127                 },
    128                 Direction::Down => if i < self.imax-1 {
    129                     Some(Position::Other { i: i+1, j })
    130                 } else {
    131                     if i == self.imax-1 && j == self.jmax-1 {
    132                         Some(Position::Finish)
    133                     } else { None }
    134                 },
    135                 Direction::Left => if j > 0 {
    136                     Some(Position::Other { i, j: j-1 })
    137                 } else { None },
    138                 Direction::Right => if j < self.jmax-1 {
    139                     Some(Position::Other { i, j: j+1 })
    140                 } else { None },
    141                 Direction::None => panic!("stupid rust")
    142             }
    143         }
    144     }
    145 
    146     fn maybe_push(&self, x: Node, d: Direction, v: &mut HashSet<Node>) {
    147         if let Some(p) = self.mv(x.p, d) {
    148             if !self.is_busy(p, x.t+1) {
    149                 v.insert(Node { p, t: x.t+1 });
    150             }
    151         }
    152     }
    153     
    154     pub fn shortest_path(
    155         &self,
    156         start: Position,
    157         finish: Position,
    158         t0: usize
    159     ) -> usize {
    160         let mut vnext = HashSet::<Node>::new();
    161         vnext.insert(Node { p: start, t: t0 });
    162         while !vnext.is_empty() {
    163             let mut v = vnext.clone().into_iter().collect::<Vec<_>>();
    164             // Heuristic: explore first the positions closer to the target
    165             v.sort_by(|a, b| a.p.dist(finish).cmp(&(b.p.dist(finish))));
    166             let mut v = VecDeque::<Node>::from(v);
    167             vnext.clear();
    168             while !v.is_empty() {
    169                 let x = v.pop_front().unwrap();
    170                 if x.p == finish { return x.t; }
    171     
    172                 self.maybe_push(x, Direction::Up, &mut vnext);
    173                 self.maybe_push(x, Direction::Down, &mut vnext);
    174                 self.maybe_push(x, Direction::Left, &mut vnext);
    175                 self.maybe_push(x, Direction::Right, &mut vnext);
    176                 if !self.is_busy(x.p, x.t+1) {
    177                     self.maybe_push(x, Direction::None, &mut vnext);
    178                 }
    179             }
    180         }
    181         panic!("queue is empty");
    182     }
    183 }
    184 
    185 pub fn read_map_from_stdin() -> Map {
    186     let mut tiles = vec![];
    187     let mut line = String::new();
    188     while std::io::stdin().read_line(&mut line).unwrap() > 0 {
    189         let bytes = line.as_bytes();
    190         if bytes[2] == b'#' {
    191             line.clear();
    192             continue;
    193         }
    194         let i = tiles.len();
    195         tiles.push(vec![]);
    196         for j in 1..bytes.len()-2 {
    197             tiles[i].push(Direction::from_byte(bytes[j]));
    198         }
    199         line.clear();
    200     }
    201     let imax = tiles.len();
    202     let jmax = tiles[0].len();
    203     Map { tiles, imax, jmax }
    204 }