#![allow(clippy::same_item_push)] use std::{ fs::File, io::{BufRead, BufReader}, path::Path, }; #[derive(Debug, Clone)] struct FS { blocks: Vec, } impl FS { fn _print_blocks(&self) { for b in &self.blocks { match b { FSBlock::File(id, size) => print!("{}", id.to_string().repeat(*size)), FSBlock::Free(size) => print!("{}", ".".repeat(*size)), } } println!(); } fn from_disk_map(disk_map: Vec) -> Self { let mut fs = FS { blocks: vec![] }; let mut current_id = 0; for (i, n) in disk_map.iter().enumerate() { if i % 2 == 0 { fs.blocks.push(FSBlock::File(current_id, *n)); current_id += 1; } else { fs.blocks.push(FSBlock::Free(*n)); } } fs } fn compact(&mut self) { let mut ignore_list = vec![]; loop { let self_tmp = self.clone(); let last_file_block = match self_tmp .blocks .iter() .enumerate() .filter_map(|(i, b)| match b { FSBlock::File(id, _) => { if ignore_list.contains(id) { None } else { Some((i, b)) } } _ => None, }) .last() { Some(b) => b, None => break, }; let (last_file_block_pos, FSBlock::File(last_file_block_id, last_file_block_size)) = last_file_block else { unreachable!() }; let first_free_block = match self_tmp .blocks .iter() .enumerate() .filter_map(|(i, b)| match b { FSBlock::Free(size) => { if size >= last_file_block_size { Some((i, b)) } else { None } } _ => None, }) .rev() .last() { Some(b) => b, None => { ignore_list.push(*last_file_block_id); continue; } }; let (first_free_block_pos, FSBlock::Free(_)) = first_free_block else { unreachable!() }; ignore_list.push(*last_file_block_id); if last_file_block_pos > first_free_block_pos { if let Some(b) = self.blocks.get_mut(first_free_block_pos) { match b { FSBlock::Free(size) => *size -= last_file_block_size, _ => unreachable!(), } } let file_block = self.blocks.remove(last_file_block_pos); self.blocks.insert(first_free_block_pos, file_block); self.blocks .insert(last_file_block_pos, FSBlock::Free(*last_file_block_size)); //self.print_blocks(); } } } fn checksum(&self) -> usize { let mut a = vec![]; for b in &self.blocks { match b { FSBlock::File(id, size) => { for _ in 0..*size { a.push(*id); } } FSBlock::Free(size) => { for _ in 0..*size { a.push(0); } } } } a.iter() .enumerate() .map(|(i, id)| i * id) .collect::>() .iter() .sum() } } #[derive(Debug, Clone)] enum FSBlock { File(usize, usize), Free(usize), } pub fn part_two(input: &Path) -> anyhow::Result { 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()) }