155 lines
4.5 KiB
Rust
155 lines
4.5 KiB
Rust
use std::collections::HashSet;
|
|
|
|
use tracing::debug;
|
|
|
|
pub fn pt1(input: &str) -> usize {
|
|
let mut disk_layout = input
|
|
.chars()
|
|
.map(|c| c.to_digit(10).unwrap())
|
|
.enumerate()
|
|
.flat_map(|(i, c)| {
|
|
(0..c)
|
|
.map(|_| if i % 2 == 0 { Some(i / 2) } else { None })
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let mut min_pos = usize::MAX;
|
|
let dl = disk_layout.clone();
|
|
debug!(
|
|
"disk layout: {}",
|
|
disk_layout
|
|
.iter()
|
|
.map(|v| match v {
|
|
Some(v) => v.to_string(),
|
|
None => ".".to_string(),
|
|
})
|
|
.collect::<String>()
|
|
);
|
|
for (pos, segment) in disk_layout.iter_mut().enumerate() {
|
|
if segment.is_none() && pos < min_pos {
|
|
let next_candidate = dl
|
|
.iter()
|
|
.enumerate()
|
|
.rev()
|
|
.find(|(i, v)| v.is_some() && *i < min_pos);
|
|
match next_candidate {
|
|
Some((i, a)) => {
|
|
segment.replace(a.unwrap());
|
|
min_pos = i;
|
|
}
|
|
None => break,
|
|
};
|
|
}
|
|
}
|
|
debug!(
|
|
"disk layout: {}",
|
|
disk_layout
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(i, _v)| *i < min_pos)
|
|
.map(|(_i, v)| match v {
|
|
Some(v) => v.to_string(),
|
|
None => ".".to_string(),
|
|
})
|
|
.collect::<String>()
|
|
);
|
|
disk_layout
|
|
.into_iter()
|
|
.enumerate()
|
|
.filter(|(i, v)| v.is_some() && *i < min_pos)
|
|
.map(|(i, v)| v.unwrap() * i)
|
|
.sum()
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) enum Segment {
|
|
Empty(usize),
|
|
File(usize, usize),
|
|
}
|
|
|
|
pub fn pt2(input: &str) -> usize {
|
|
let mut final_disk: Vec<usize> = Vec::new();
|
|
let disk_layout = input
|
|
.chars()
|
|
.map(|c| c.to_digit(10).unwrap())
|
|
.enumerate()
|
|
.map(|(i, c)| {
|
|
if i % 2 == 0 {
|
|
Segment::File(c as usize, i / 2)
|
|
} else {
|
|
Segment::Empty(c as usize)
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let mut moved_segments = HashSet::new();
|
|
for (outer_pos, segment) in disk_layout.iter().enumerate() {
|
|
let mut s = segment.clone();
|
|
if moved_segments.contains(&outer_pos) {
|
|
s = match s {
|
|
Segment::File(v, _i) => Segment::Empty(v),
|
|
Segment::Empty(v) => Segment::Empty(v),
|
|
};
|
|
}
|
|
moved_segments.insert(outer_pos);
|
|
match s {
|
|
Segment::File(v, i) => {
|
|
final_disk.append(&mut vec![i; v]);
|
|
}
|
|
Segment::Empty(v) => {
|
|
let mut remaining_space = v;
|
|
while remaining_space > 0 {
|
|
if let Some((pos, v, i)) = disk_layout
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(_pos, s)| match s {
|
|
Segment::Empty(_) => false,
|
|
Segment::File(_v, _i) => true,
|
|
})
|
|
.filter(|(pos, _s)| !moved_segments.contains(pos))
|
|
.map(|(pos, s)| match s {
|
|
Segment::Empty(_) => unreachable!(),
|
|
Segment::File(v, i) => (pos, *v, *i),
|
|
})
|
|
.rev()
|
|
.find(|(_pos, v, _i)| *v <= remaining_space)
|
|
{
|
|
final_disk.append(&mut vec![i; v]);
|
|
remaining_space -= v;
|
|
moved_segments.insert(pos);
|
|
} else {
|
|
final_disk.append(&mut vec![0; remaining_space]);
|
|
remaining_space = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
debug!("disk layout: {final_disk:?}");
|
|
final_disk.iter().enumerate().map(|(a, b)| a * *b).sum()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use tracing_test::traced_test;
|
|
|
|
use super::{pt1, pt2};
|
|
use std::fs::read_to_string;
|
|
|
|
#[traced_test]
|
|
#[test]
|
|
pub fn test_pt1() {
|
|
let input =
|
|
read_to_string("./inputs/day09/example.txt").expect("Missing example.txt for day09");
|
|
assert_eq!(pt1(&input), 1928)
|
|
}
|
|
|
|
#[traced_test]
|
|
#[test]
|
|
pub fn test_pt2() {
|
|
let input =
|
|
read_to_string("./inputs/day09/example.txt").expect("Missing example.txt for day09");
|
|
|
|
assert_eq!(pt2(&input), 2858)
|
|
}
|
|
}
|