A input/day9/example => input/day9/example +1 -0
@@ 0,0 1,1 @@
+2333133121414131402
A src/day9/mod.rs => src/day9/mod.rs +11 -0
@@ 0,0 1,11 @@
+use std::path::Path;
+
+mod part1;
+mod part2;
+
+pub fn solve(input: &Path) -> anyhow::Result<()> {
+ println!("part one: {}", part1::part_one(input)?);
+ println!("part two: {}", part2::part_two(input)?);
+
+ Ok(())
+}
A src/day9/part1.rs => src/day9/part1.rs +109 -0
@@ 0,0 1,109 @@
+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())
+}
A src/day9/part2.rs => src/day9/part2.rs +159 -0
@@ 0,0 1,159 @@
+#![allow(clippy::same_item_push)]
+use std::{
+ fs::File,
+ io::{BufRead, BufReader},
+ path::Path,
+};
+
+#[derive(Debug, Clone)]
+struct FS {
+ blocks: Vec<FSBlock>,
+}
+
+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<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 {
+ 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::<Vec<usize>>()
+ .iter()
+ .sum()
+ }
+}
+
+#[derive(Debug, Clone)]
+enum FSBlock {
+ File(usize, usize),
+ Free(usize),
+}
+
+pub fn part_two(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())
+}
M src/main.rs => src/main.rs +8 -0
@@ 10,6 10,7 @@ mod day5;
mod day6;
mod day7;
mod day8;
+mod day9;
#[derive(Parser)]
struct Args {
@@ 51,6 52,10 @@ enum DayArgs {
#[arg(short)]
input: PathBuf,
},
+ Day9 {
+ #[arg(short)]
+ input: PathBuf,
+ },
}
fn main() -> anyhow::Result<()> {
@@ 81,6 86,9 @@ fn main() -> anyhow::Result<()> {
DayArgs::Day8 { input } => {
day8::solve(&input)?;
}
+ DayArgs::Day9 { input } => {
+ day9::solve(&input)?;
+ }
}
Ok(())