Added day 23 code for part 1 and 2

Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
Louis Vallat 2022-01-12 12:09:44 +01:00
parent 1cfc6e940e
commit bf62fa4e41
No known key found for this signature in database
GPG Key ID: 0C87282F76E61283
4 changed files with 233 additions and 0 deletions

View File

@ -113,3 +113,8 @@ day-22:
stage: build
script:
- cd day22; cargo run --release ./input
day-23:
stage: build
script:
- cd day23; cargo run --release ./input

8
day23/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "day23"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

5
day23/input Normal file
View File

@ -0,0 +1,5 @@
#############
#...........#
###A#D#B#D###
#B#C#A#C#
#########

215
day23/src/main.rs Normal file
View File

@ -0,0 +1,215 @@
use std::{fs, env, collections::{HashSet, HashMap}};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum Amphipod {
Amber,
Bronze,
Copper,
Desert,
Placeholder
}
impl Amphipod {
fn get_target_room(&self) -> usize {
return match self {
Amphipod::Amber => { 0 }
Amphipod::Bronze => { 1 }
Amphipod::Copper => { 2 }
Amphipod::Desert => { 3 }
_ => { panic!("Amphipod has no target room.") }
};
}
fn get_from_str(s: &str) -> Amphipod {
return match s {
"A" => { Amphipod::Amber }
"B" => { Amphipod::Bronze }
"C" => { Amphipod::Copper }
"D" => { Amphipod::Desert }
_ => { panic!("Amphipod not recognized.") }
};
}
}
type Action = ((usize, bool), (usize, bool), u32);
fn read_input(path: &str) -> String {
return fs::read_to_string(path).expect("Cannot read file.");
}
fn parse_input(s: &str) -> Vec<Vec<Amphipod>> {
let l = s.lines().skip(2)
.map(|l| l.trim().split("#").filter(|e| !e.is_empty())
.collect::<Vec<&str>>())
.filter(|e| !e.is_empty())
.collect::<Vec<Vec<&str>>>();
let mut g = vec![
vec![Amphipod::Placeholder; l.len()];
l.iter().fold(0, |acc, e| std::cmp::max(e.len(), acc))];
for (n, v) in l.iter().rev().enumerate() {
for (i, a) in v.iter().enumerate() {
g[i][n] = Amphipod::get_from_str(*a);
}
}
return g;
}
fn expand_input(r: &mut Vec<Vec<Amphipod>>) {
let mut s = vec![
Amphipod::Amber, Amphipod::Copper,
Amphipod::Bronze, Amphipod::Amber,
Amphipod::Copper, Amphipod::Bronze,
Amphipod::Desert, Amphipod::Desert];
for e in r.iter_mut() {
e.insert(1, s.pop().unwrap());
e.insert(2, s.pop().unwrap());
}
}
fn no_need_to_move(r: &Vec<Vec<Amphipod>>) -> HashSet<(usize, usize)> {
let mut h = HashSet::new();
for (i, v) in r.iter().enumerate() {
for (l, a) in v.iter().enumerate() {
if a.get_target_room() == i && (l == 0 || h.contains(&(i, l - 1))) {
h.insert((i, l));
}
}
}
return h;
}
fn is_room_full(rooms: &Vec<Vec<Amphipod>>, n: usize, s: &HashSet<(usize, usize)>,
room_depth: usize) -> bool {
return rooms[n].len() == room_depth
&& s.iter().filter(|e| e.0 == n).count() == room_depth;
}
fn is_target_room_ready(rooms: &Vec<Vec<Amphipod>>, n: usize, s: &HashSet<(usize, usize)>,
room_depth: usize) -> bool {
return !is_room_full(rooms, n, s, room_depth)
&& s.iter().filter(|e| e.0 == n).count() == rooms[n].len();
}
fn can_cross_hallway(s: usize, t: usize, h: &Vec<Option<Amphipod>>) -> bool {
if h.iter().all(|e| e.is_none()) { return true; }
if s == t { return false; }
let (b, e) = (std::cmp::min(s, t) + 1, std::cmp::max(s, t));
return (b..e).all(|e| h[e].is_none());
}
fn room_to_hallway_pos(n: usize) -> usize {
return n * 2 + 2;
}
fn get_move_cost(f: &(usize, bool), t: &(usize, bool), r: &Vec<Vec<Amphipod>>,
d: usize, a: &Amphipod) -> u32 {
let s = if f.1 { room_to_hallway_pos(f.0) } else { f.0 } as i32;
let e = if t.1 { room_to_hallway_pos(t.0) } else { t.0 } as i32;
return ((s - e).abs() as u32
+ if f.1 { (d - r[f.0].len()) as u32 + 1 } else { 0 }
+ if t.1 { (d - r[t.0].len()) as u32 } else { 0 })
* 10u32.pow(a.get_target_room() as u32);
}
fn get_next_moves(rs: &Vec<Vec<Amphipod>>, d: usize, h: &Vec<Option<Amphipod>>,
s: &HashSet<(usize, usize)>) -> Vec<Action>{
let f = (0..rs.len()).map(|e| room_to_hallway_pos(e)).collect::<Vec<usize>>();
let mut a = vec![];
for (i, e) in h.iter().enumerate() {
if e.is_none() { continue; }
let e = e.unwrap();
let t = e.get_target_room();
if is_target_room_ready(rs, t, s, d)
&& can_cross_hallway(i, room_to_hallway_pos(t), h) {
return vec![((
(i, false),
(t, true),
get_move_cost(&(i, false), &(t, true), rs, d, &e)
))];
}
}
for (n, r) in rs.iter().enumerate() {
if r.is_empty() { continue; }
let l = r.last().unwrap();
let t = l.get_target_room();
if l.get_target_room() != n
&& is_target_room_ready(rs, t, s, d)
&& can_cross_hallway(room_to_hallway_pos(n), room_to_hallway_pos(t), h) {
return vec![((
(n, true),
(t, true),
get_move_cost(&(n, true), &(t, true), rs, d, &l))
)];
}
if !is_room_full(rs, n, s, d) && !is_target_room_ready(rs, n, s, d) {
for (j, o) in h.iter().enumerate() {
if o.is_some() || f.contains(&j) { continue; }
if can_cross_hallway(room_to_hallway_pos(n), j, h) {
a.push((
(n, true),
(j, false),
get_move_cost(&(n, true), &(j, false), rs, d, &l)));
}
}
}
}
return a;
}
fn apply_action(a: &Action, r: &mut Vec<Vec<Amphipod>>, s: &mut u32,
h: &mut Vec<Option<Amphipod>>) {
*s += a.2;
let o;
if a.0.1 {
o = r[a.0.0].pop().unwrap();
} else {
o = h[a.0.0].unwrap();
h[a.0.0] = None;
}
if a.1.1 {
r[a.1.0].push(o);
} else {
h[a.1.0] = Some(o);
}
}
fn compute_sorting(mut r: Vec<Vec<Amphipod>>, mut h: Vec<Option<Amphipod>>, d: usize,
mut s: u32, b: &mut u32, a: &Action,
c: &mut HashMap<(Vec<Vec<Amphipod>>, Vec<Option<Amphipod>>), u32>) -> Option<u32> {
apply_action(a, &mut r, &mut s, &mut h);
if s >= *c.get(&(r.clone(), h.clone())).unwrap_or(&u32::MAX) { return None; }
c.insert((r.clone(), h.clone()), s);
let n = no_need_to_move(&r);
if n.len() == r.len() * d {
*b = s;
return Some(s);
}
let m = get_next_moves(&r, d, &h, &n);
if m.is_empty() { return None; }
let mut o = None;
for mo in m {
let x = compute_sorting(r.clone(), h.clone(), d, s, b, &mo, c);
if x.is_some() && (o.is_none() || x.unwrap() < o.unwrap()) { o = x; }
}
return o;
}
fn main() {
let args: Vec<String> = env::args().collect();
for arg in args.iter().skip(1) {
let input = read_input(&arg);
let mut vec_in = parse_input(&input);
let (mut s1, mut s2) = (u32::MAX, u32::MAX);
let a = ((0, true), (0, true), 0);
let mut c = HashMap::new();
let h = vec![None; 11];
compute_sorting(vec_in.clone(), h.clone(), 2, 0, &mut s1, &a, &mut c);
expand_input(&mut vec_in);
c.clear();
compute_sorting(vec_in, h, 4, 0, &mut s2, &a, &mut c);
println!("[{}]", &arg);
println!("\t[Part 1] => Answer is '{}'.", s1);
println!("\t[Part 2] => Answer is '{}'.", s2);
}
}