Added day 23 code for part 1 and 2
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
parent
1cfc6e940e
commit
bf62fa4e41
@ -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
8
day23/Cargo.toml
Normal 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
5
day23/input
Normal file
@ -0,0 +1,5 @@
|
||||
#############
|
||||
#...........#
|
||||
###A#D#B#D###
|
||||
#B#C#A#C#
|
||||
#########
|
215
day23/src/main.rs
Normal file
215
day23/src/main.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user