adventofcode

git clone https://git.ce9e.org/adventofcode.git

commit
afb9f7d85386897979a9d5b4ef5144ae967a13bb
parent
1373a55323e994d8d22f6789767311f18feafdcd
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2022-12-25 17:01
2022-12-24 part2

Diffstat

M 2022/24/solution.rs 142 +++++++++++++++++++++++++++++++++++--------------------------

1 files changed, 81 insertions, 61 deletions


diff --git a/2022/24/solution.rs b/2022/24/solution.rs

@@ -1,4 +1,6 @@
    1     1 use std::collections::HashSet;
   -1     2 use std::collections::binary_heap::BinaryHeap;
   -1     3 use std::cmp::Reverse;
    2     4 
    3     5 #[path = "../lib.rs"] mod lib;
    4     6 
@@ -10,15 +12,25 @@ struct Blizzard {
   10    12     dir: Dir,
   11    13 }
   12    14 
   13    -1 fn get_input() -> (usize, usize, Vec<Blizzard>) {
   -1    15 struct Map {
   -1    16     width: usize,
   -1    17     height: usize,
   -1    18     blizzards: Vec<Blizzard>,
   -1    19 }
   -1    20 
   -1    21 fn abs_diff(a: usize, b: usize) -> usize {
   -1    22     return a.max(b) - a.min(b);
   -1    23 }
   -1    24 
   -1    25 fn get_input() -> Map {
   14    26     let mut blizzards = vec![];
   15    -1     let mut width = 0;
   16    -1     let mut height = 0;
   -1    27     let mut max_x = 0;
   -1    28     let mut max_y = 0;
   17    29 
   18    30     for (y, line) in lib::iter_input().enumerate() {
   19    -1         height = y;
   -1    31         max_y = y;
   20    32         for (x, c) in line.chars().enumerate() {
   21    -1             width = x;
   -1    33             max_x = x;
   22    34             match c {
   23    35                 '.' => {},
   24    36                 '#' => {},
@@ -39,89 +51,97 @@ fn get_input() -> (usize, usize, Vec<Blizzard>) {
   39    51         }
   40    52     }
   41    53 
   42    -1     return (width - 1, height - 1, blizzards);
   43    -1 }
   44    -1 
   45    -1 fn format(width: usize, height: usize, blizzards: &Vec<Blizzard>, i: usize, pos: (usize, usize)) {
   46    -1     for y in 0..height {
   47    -1         for x in 0..width {
   48    -1             let k = blizzards.iter().filter(|b| has_pos(width, height, b, i, x, y)).count();
   49    -1             if (x, y) == pos {
   50    -1                 assert_eq!(k, 0);
   51    -1                 print!("E");
   52    -1             } else if k > 0 {
   53    -1                 print!("{}", k);
   54    -1             } else {
   55    -1                 print!(".");
   56    -1             }
   57    -1         }
   58    -1         print!("\n");
   59    -1     }
   60    -1     print!("\n");
   61    -1 }
   62    -1 
   63    -1 fn has_pos(width: usize, height: usize, blizzard: &Blizzard, i: usize, x: usize, y: usize) -> bool {
   64    -1     return match blizzard.dir {
   65    -1         Dir::Up => x == blizzard.x0 && y == (blizzard.y0 + (height - 1) * i) % height,
   66    -1         Dir::Right => y == blizzard.y0 && x == (blizzard.x0 + i) % width,
   67    -1         Dir::Down => x == blizzard.x0 && y == (blizzard.y0 + i) % height,
   68    -1         Dir::Left => y == blizzard.y0 && x == (blizzard.x0 + (width - 1) * i) % width,
   -1    54     return Map {
   -1    55         width: max_x - 1,
   -1    56         height: max_y - 1,
   -1    57         blizzards: blizzards,
   69    58     };
   70    59 }
   71    60 
   72    -1 fn part1(width: usize, height: usize, blizzards: &Vec<Blizzard>) -> usize {
   73    -1     let mut offset = 1;
   74    -1     while blizzards.iter().any(|b| has_pos(width, height, b, offset, 0, 0)) {
   75    -1         offset += 1;
   76    -1     }
   -1    61 fn is_blocked(map: &Map, x: usize, y: usize, i: usize) -> bool {
   -1    62     return map.blizzards.iter().any(|blizzard| match blizzard.dir {
   -1    63         Dir::Up => x == blizzard.x0 && y == (blizzard.y0 + (map.height - 1) * i) % map.height,
   -1    64         Dir::Right => y == blizzard.y0 && x == (blizzard.x0 + i) % map.width,
   -1    65         Dir::Down => x == blizzard.x0 && y == (blizzard.y0 + i) % map.height,
   -1    66         Dir::Left => y == blizzard.y0 && x == (blizzard.x0 + (map.width - 1) * i) % map.width,
   -1    67     });
   -1    68 }
   77    69 
   78    -1     let mut best_i = usize::MAX;
   -1    70 fn find_path(map: &Map, start: (usize, usize), end: (usize, usize), offset: usize) -> usize {
   -1    71     let mut best_i = usize::MAX - 1;
   79    72     let mut seen = HashSet::new();
   80    -1     let mut queue = vec![
   81    -1         (0, 0, offset),
   82    -1     ];
   -1    73     let mut queue = BinaryHeap::new();
   -1    74     queue.push((
   -1    75         Reverse(usize::MAX - 2),
   -1    76         usize::MAX,
   -1    77         usize::MAX,
   -1    78         offset,
   -1    79     ));
   -1    80     let mut k = 0;
   -1    81 
   -1    82     while let Some((Reverse(min_i), x, y, i)) = queue.pop() {
   -1    83         if min_i >= best_i {
   -1    84             // for some reason, continue is faster than break/return
   -1    85             continue;
   -1    86         }
   83    87 
   84    -1     while let Some((x, y, i)) = queue.pop() {
   85    88         if seen.contains(&(x, y, i)) {
   86    89             continue;
   87    90         } else {
   88    91             seen.insert((x, y, i));
   89    92         }
   90    93 
   91    -1         if i + (width - 1 - x) + (height - 1 - y) >= best_i {
   -1    94         if is_blocked(map, x, y, i) {
   92    95             continue;
   93    96         }
   94    -1         if x == width - 1 && y == height - 1 {
   95    -1             println!("{} {}", i, queue.len());
   -1    97 
   -1    98         if x == end.0 && y == end.1 {
   96    99             best_i = i;
   97   100             continue;
   98   101         }
   -1   102         k += 1;
   99   103 
  100   104         let mut moves = vec![(x, y)];
  101    -1         if x > 0 {
  102    -1             moves.push((x - 1, y));
  103    -1         }
  104    -1         if y > 0 {
  105    -1             moves.push((x, y - 1));
  106    -1         }
  107    -1         if x + 1 < width {
  108    -1             moves.push((x + 1, y));
  109    -1         }
  110    -1         if y + 1 < height {
  111    -1             moves.push((x, y + 1));
   -1   105         if x == usize::MAX && y == usize::MAX {
   -1   106             moves.push(start);
   -1   107         } else {
   -1   108             if x + 1 < map.width {
   -1   109                 moves.push((x + 1, y));
   -1   110             }
   -1   111             if y + 1 < map.height {
   -1   112                 moves.push((x, y + 1));
   -1   113             }
   -1   114             if x > 0 {
   -1   115                 moves.push((x - 1, y));
   -1   116             }
   -1   117             if y > 0 {
   -1   118                 moves.push((x, y - 1));
   -1   119             }
  112   120         }
  113   121 
  114   122         for (xx, yy) in moves {
  115    -1             if !blizzards.iter().any(|b| has_pos(width, height, b, i + 1, xx, yy)) {
  116    -1                 queue.push((xx, yy, i + 1));
  117    -1             }
   -1   123             queue.push((
   -1   124                 Reverse((i + 1) + abs_diff(xx, end.0) + abs_diff(yy, end.1)),
   -1   125                 xx,
   -1   126                 yy,
   -1   127                 i + 1,
   -1   128             ));
  118   129         }
  119   130     }
  120   131 
   -1   132     println!("{}", k);
  121   133     return best_i + 1;
  122   134 }
  123   135 
  124   136 fn main() {
  125    -1     let (width, height, blizzards) = get_input();
  126    -1     println!("part1: {}", part1(width, height, &blizzards));
   -1   137     let map = get_input();
   -1   138     let start = (0, 0);
   -1   139     let end = (map.width - 1, map.height - 1);
   -1   140 
   -1   141     let t1 = find_path(&map, start, end, 0);
   -1   142     println!("part1: {}", t1);
   -1   143 
   -1   144     let t2 = find_path(&map, end, start, t1);
   -1   145     let t3 = find_path(&map, start, end, t2);
   -1   146     println!("part2: {}", t3);
  127   147 }