use std::{
fs::File,
io::{BufRead, BufReader},
path::Path,
};
#[derive(Debug)]
struct FS {
blocks: Vec<FSBlock>,
}
impl FS {
fn _print_blocks(&self) {
for b in &self.blocks {
match b {
FSBlock::File(id) => print!("{}", id),
FSBlock::Free => print!("."),
}
}
println!();
}
fn from_disk_map(disk_map: Vec<usize>) -> Self {
let mut fs = FS { blocks: vec![] };
let mut current_id = 0;
for (i, n) in disk_map.iter().enumerate() {
if i % 2 == 0 {
for _ in 0..*n {
fs.blocks.push(FSBlock::File(current_id));
}
current_id += 1;
} else {
for _ in 0..*n {
fs.blocks.push(FSBlock::Free);
}
}
}
fs
}
fn compact(&mut self) {
loop {
let last_file_block = self
.blocks
.iter()
.enumerate()
.filter_map(|(i, b)| match b {
FSBlock::File(_) => Some(i),
_ => None,
})
.last()
.unwrap();
let first_free_block = self
.blocks
.iter()
.enumerate()
.filter_map(|(i, b)| match b {
FSBlock::Free => Some(i),
_ => None,
})
.rev()
.last()
.unwrap();
if last_file_block > first_free_block {
self.blocks.swap(first_free_block, last_file_block);
//self.print_blocks();
} else {
break;
}
}
}
fn checksum(&self) -> usize {
self.blocks
.iter()
.enumerate()
.filter_map(|(i, b)| match b {
FSBlock::File(id) => Some(i * id),
_ => None,
})
.collect::<Vec<usize>>()
.iter()
.sum()
}
}
#[derive(Debug, Clone)]
enum FSBlock {
File(usize),
Free,
}
pub fn part_one(input: &Path) -> anyhow::Result<usize> {
let mut reader = BufReader::new(File::open(input)?);
let mut input_str = String::new();
let _ = reader.read_line(&mut input_str)?;
let input = input_str
.trim()
.chars()
.map(|c| -> usize { c.to_string().parse().unwrap() })
.collect();
let mut fs = FS::from_disk_map(input);
//fs.print_blocks();
fs.compact();
Ok(fs.checksum())
}