adventofcode

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

commit
fada144f3d89c89b86a9b8df04e0053ebd22b1ff
parent
47fe721a55f6a72468ec12d9ed90cb4235b4cde7
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2021-12-23 15:07
day 23 part 2

Diffstat

A 2021/23/input2.txt 7 +++++++
A 2021/23/part2.rs 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A 2021/23/test2.txt 7 +++++++

3 files changed, 273 insertions, 0 deletions


diff --git a/2021/23/input2.txt b/2021/23/input2.txt

@@ -0,0 +1,7 @@
   -1     1 #############
   -1     2 #...........#
   -1     3 ###C#A#B#D###
   -1     4   #D#C#B#A#
   -1     5   #D#B#A#C#
   -1     6   #B#A#D#C#
   -1     7   #########

diff --git a/2021/23/part2.rs b/2021/23/part2.rs

@@ -0,0 +1,259 @@
   -1     1 use std::collections::HashMap;
   -1     2 use std::env::args;
   -1     3 use std::fs::File;
   -1     4 use std::io::BufRead;
   -1     5 use std::io::BufReader;
   -1     6 
   -1     7 const LEN: usize = 23;
   -1     8 
   -1     9 // #############
   -1    10 // #01.2.3.4.56#
   -1    11 // ###7#8#9#0###
   -1    12 //   #1#2#3#4#
   -1    13 //   #5#6#7#8#
   -1    14 //   #9#0#1#2#
   -1    15 //   #########
   -1    16 
   -1    17 fn i2xy(i: usize) -> (i64, i64) {
   -1    18     match i {
   -1    19         0 => (1, 1),
   -1    20         1 => (2, 1),
   -1    21         2 => (4, 1),
   -1    22         3 => (6, 1),
   -1    23         4 => (8, 1),
   -1    24         5 => (10, 1),
   -1    25         6 => (11, 1),
   -1    26         7 => (3, 2),
   -1    27         8 => (5, 2),
   -1    28         9 => (7, 2),
   -1    29         10 => (9, 2),
   -1    30         11 => (3, 3),
   -1    31         12 => (5, 3),
   -1    32         13 => (7, 3),
   -1    33         14 => (9, 3),
   -1    34         15 => (3, 4),
   -1    35         16 => (5, 4),
   -1    36         17 => (7, 4),
   -1    37         18 => (9, 4),
   -1    38         19 => (3, 5),
   -1    39         20 => (5, 5),
   -1    40         21 => (7, 5),
   -1    41         22 => (9, 5),
   -1    42         _ => unreachable!(),
   -1    43     }
   -1    44 }
   -1    45 
   -1    46 fn xy2i(x: i64, y: i64) -> Option<usize> {
   -1    47     match (x, y) {
   -1    48         (1, 1) => Some(0),
   -1    49         (2, 1) => Some(1),
   -1    50         (4, 1) => Some(2),
   -1    51         (6, 1) => Some(3),
   -1    52         (8, 1) => Some(4),
   -1    53         (10, 1) => Some(5),
   -1    54         (11, 1) => Some(6),
   -1    55         (3, 2) => Some(7),
   -1    56         (5, 2) => Some(8),
   -1    57         (7, 2) => Some(9),
   -1    58         (9, 2) => Some(10),
   -1    59         (3, 3) => Some(11),
   -1    60         (5, 3) => Some(12),
   -1    61         (7, 3) => Some(13),
   -1    62         (9, 3) => Some(14),
   -1    63         (3, 4) => Some(15),
   -1    64         (5, 4) => Some(16),
   -1    65         (7, 4) => Some(17),
   -1    66         (9, 4) => Some(18),
   -1    67         (3, 5) => Some(19),
   -1    68         (5, 5) => Some(20),
   -1    69         (7, 5) => Some(21),
   -1    70         (9, 5) => Some(22),
   -1    71         _ => None,
   -1    72     }
   -1    73 }
   -1    74 
   -1    75 fn path_steps(i: usize, j: usize) -> i64 {
   -1    76     let (x1, y1) = i2xy(i);
   -1    77     let (x2, y2) = i2xy(j);
   -1    78     if x1 == x2 {
   -1    79         return (y1 - y2).abs();
   -1    80     } else {
   -1    81         return (x1 - x2).abs() + (y1 - 1).abs() + (y2 - 1).abs();
   -1    82     }
   -1    83 }
   -1    84 
   -1    85 fn path_free(map: &[u8; LEN], i: usize, j: usize) -> bool {
   -1    86     let (x1, y1) = i2xy(i);
   -1    87     let (x2, y2) = i2xy(j);
   -1    88 
   -1    89     for y in 2..y1 {
   -1    90         if map[xy2i(x1, y).unwrap()] != 0 {
   -1    91             return false;
   -1    92         }
   -1    93     }
   -1    94     for y in 2..y2 {
   -1    95         if map[xy2i(x2, y).unwrap()] != 0 {
   -1    96             return false;
   -1    97         }
   -1    98     }
   -1    99 
   -1   100     let (x_min, x_max) = if x1 < x2 {(x1, x2)} else {(x2, x1)};
   -1   101     for x in x_min+1..x_max {
   -1   102         match xy2i(x, 1) {
   -1   103             Some(a) => if map[a] != 0 {
   -1   104                 return false;
   -1   105             },
   -1   106             None => {},
   -1   107         }
   -1   108     }
   -1   109 
   -1   110     return true
   -1   111 }
   -1   112 
   -1   113 fn is_final_position(map: &[u8; LEN], c: u8, x: i64, y: i64) -> bool {
   -1   114     if y == 1 {
   -1   115         return false;
   -1   116     }
   -1   117     if x / 2 != i64::from(c) {
   -1   118         return false;
   -1   119     }
   -1   120     for yy in y+1.. {
   -1   121         match xy2i(x, yy) {
   -1   122             Some(a) => if map[a] != c {
   -1   123                 return false;
   -1   124             },
   -1   125             None => break,
   -1   126         }
   -1   127     }
   -1   128     return true;
   -1   129 }
   -1   130 
   -1   131 fn legal_move(map: &[u8; LEN], i: usize, j: usize) -> bool {
   -1   132     if map[j] != 0 {
   -1   133         return false;
   -1   134     }
   -1   135 
   -1   136     let (x1, y1) = i2xy(i);
   -1   137     let (x2, y2) = i2xy(j);
   -1   138 
   -1   139     // cannot move from hallway to hallway
   -1   140     if y1 == 1 && y2 == 1 {
   -1   141         return false;
   -1   142     }
   -1   143 
   -1   144     if y2 > 1 && !is_final_position(map, map[i], x2, y2) {
   -1   145         return false;
   -1   146     }
   -1   147 
   -1   148     if is_final_position(map, map[i], x1, y1) {
   -1   149         return false;
   -1   150     }
   -1   151 
   -1   152     if !path_free(map, i, j) {
   -1   153         return false;
   -1   154     }
   -1   155 
   -1   156     return true;
   -1   157 }
   -1   158 
   -1   159 fn get_input() -> ([u8; LEN], [u8; LEN]) {
   -1   160     let path = args().nth(1).unwrap();
   -1   161     let file = File::open(path).unwrap();
   -1   162 
   -1   163     let mut map = [0; LEN];
   -1   164     let mut target = [0; LEN];
   -1   165 
   -1   166     for (y, line) in BufReader::new(file).lines().enumerate() {
   -1   167         for (x, c) in line.unwrap().chars().enumerate() {
   -1   168             match (xy2i(x as i64, y as i64), c)  {
   -1   169                 (Some(i), 'A') => map[i] = 1,
   -1   170                 (Some(i), 'B') => map[i] = 2,
   -1   171                 (Some(i), 'C') => map[i] = 3,
   -1   172                 (Some(i), 'D') => map[i] = 4,
   -1   173                 _ => {},
   -1   174             }
   -1   175         }
   -1   176     }
   -1   177 
   -1   178     for c in 1..=4 {
   -1   179         for y in 2.. {
   -1   180             match xy2i(c * 2 + 1, y) {
   -1   181                 Some(i) => target[i] = c as u8,
   -1   182                 None => break,
   -1   183             }
   -1   184         }
   -1   185     }
   -1   186 
   -1   187     return (map, target);
   -1   188 }
   -1   189 
   -1   190 fn legal_steps(map: &[u8; LEN]) -> Vec<([u8; LEN], i64)> {
   -1   191     let mut steps = vec![];
   -1   192     for i in 0..LEN {
   -1   193         if map[i] != 0 {
   -1   194             for j in 0..LEN {
   -1   195                 if legal_move(&map, i, j) {
   -1   196                     let mut m = map.clone();
   -1   197                     m[j] = map[i];
   -1   198                     m[i] = 0;
   -1   199                     let e = path_steps(i, j) * 10i64.pow(u32::from(map[i]) - 1);
   -1   200                     steps.push((m, e));
   -1   201                 }
   -1   202             }
   -1   203         }
   -1   204     }
   -1   205     return steps;
   -1   206 }
   -1   207 
   -1   208 fn estimate(map: &[u8; LEN]) -> i64 {
   -1   209     // lower bound for required energy
   -1   210     let mut required = 0;
   -1   211     for i in 0..LEN {
   -1   212         let (x, y) = i2xy(i);
   -1   213         if map[i] != 0 && !is_final_position(map, map[i], x, y) {
   -1   214             let j = xy2i(i64::from(map[i]) * 2 + 1, 2).unwrap();
   -1   215             required += path_steps(i, j) * 10i64.pow(u32::from(map[i]) - 1);
   -1   216         }
   -1   217     }
   -1   218     return required;
   -1   219 }
   -1   220 
   -1   221 fn better(a: i64, b: Option<&i64>) -> bool {
   -1   222     match b {
   -1   223         Some(bb) => a < *bb,
   -1   224         None => true,
   -1   225     }
   -1   226 }
   -1   227 
   -1   228 fn main() {
   -1   229     let (initial, target) = get_input();
   -1   230     let mut queue = vec![];
   -1   231     let mut energies = HashMap::new();
   -1   232     let mut estimates = HashMap::new();
   -1   233 
   -1   234     queue.push(initial);
   -1   235     energies.insert(initial, 0);
   -1   236     estimates.insert(initial, 0);
   -1   237 
   -1   238     while queue.len() > 0 {
   -1   239         let map = queue.remove(0);
   -1   240         for (m, e_new) in legal_steps(&map) {
   -1   241             let e = e_new + energies.get(&map).unwrap();
   -1   242             let ee = e + estimate(&m);
   -1   243             if better(e, energies.get(&m)) && better(ee, energies.get(&target)) {
   -1   244                 energies.insert(m, e);
   -1   245                 estimates.insert(m, ee);
   -1   246                 if m == target {
   -1   247                     queue = queue.into_iter().filter(|map| *estimates.get(map).unwrap() < e).collect();
   -1   248                 } else {
   -1   249                     match queue.binary_search_by_key(&ee, |map| *estimates.get(map).unwrap()) {
   -1   250                         Ok(i) => queue.insert(i, m),
   -1   251                         Err(i) => queue.insert(i, m),
   -1   252                     }
   -1   253                 }
   -1   254             }
   -1   255         }
   -1   256     }
   -1   257 
   -1   258     println!("{}", energies.get(&target).unwrap());
   -1   259 }

diff --git a/2021/23/test2.txt b/2021/23/test2.txt

@@ -0,0 +1,7 @@
   -1     1 #############
   -1     2 #...........#
   -1     3 ###B#C#B#D###
   -1     4   #D#C#B#A#
   -1     5   #D#B#A#C#
   -1     6   #A#D#C#A#
   -1     7   #########