Skip to content

Commit 7b0f800

Browse files
committed
solve: day 9
1 parent c03812d commit 7b0f800

File tree

4 files changed

+174
-1
lines changed

4 files changed

+174
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
resolver = "2"
33
members = [
44
"utils",
5-
"aoc_derive", "day1", "day2", "day3", "day4", "day5", "day6", "day7", "day8",
5+
"aoc_derive", "day1", "day2", "day3", "day4", "day5", "day6", "day7", "day8", "day9",
66
]
77

88
[workspace.dependencies]

day9/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "day9"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
aoc_derive.path = '../aoc_derive'
8+
utils.path = '../utils'
9+
derive_more.workspace = true
10+
itertools.workspace = true
11+
lazy-regex.workspace = true
12+
parse-display.workspace = true
13+
rayon.workspace = true
14+
regex.workspace = true
15+
num.workspace = true
16+
17+
[dev-dependencies]
18+
pretty_assertions.workspace = true

day9/src/main.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#![feature(let_chains)]
2+
3+
use std::str::FromStr;
4+
5+
use aoc_derive::aoc_main;
6+
use utils::*;
7+
8+
fn part1(mut disk: Vec<Option<usize>>) -> usize {
9+
loop {
10+
let first_none =
11+
disk.iter().enumerate().find_map(|(i, c)| c.is_none().then_some(i)).unwrap();
12+
13+
if disk[first_none..].iter().all(|c| c.is_none()) {
14+
return disk.iter().enumerate().flat_map(|(i, c)| c.map(|c| i * c)).sum();
15+
}
16+
17+
let back_index =
18+
disk.iter().enumerate().rev().find_map(|(i, c)| c.is_some().then_some(i)).unwrap();
19+
let insert_index =
20+
disk.iter().enumerate().find_map(|(i, c)| c.is_none().then_some(i)).unwrap();
21+
22+
disk.swap(back_index, insert_index);
23+
}
24+
}
25+
26+
#[derive(Debug, Clone)]
27+
struct File {
28+
id: usize,
29+
size: usize,
30+
tried_to_move: bool,
31+
}
32+
33+
#[derive(Debug, Clone, derive_more::Unwrap)]
34+
#[unwrap(owned, ref, ref_mut)]
35+
enum Block {
36+
Space(usize),
37+
File(File),
38+
}
39+
40+
#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
41+
struct Disk {
42+
blocks: Vec<Block>,
43+
}
44+
45+
impl FromStr for Disk {
46+
type Err = ();
47+
48+
fn from_str(s: &str) -> Result<Self, Self::Err> {
49+
Ok(Self {
50+
blocks: s
51+
.trim()
52+
.chars()
53+
.enumerate()
54+
.map(|(i, c)| {
55+
let size = c.to_string().parse().unwrap();
56+
if i % 2 == 0 {
57+
Block::File(File { id: i / 2, size, tried_to_move: false })
58+
} else {
59+
Block::Space(size)
60+
}
61+
})
62+
.collect(),
63+
})
64+
}
65+
}
66+
67+
impl Disk {
68+
fn checksum(&self) -> usize {
69+
self.blocks
70+
.iter()
71+
.filter(|block| !matches!(block, Block::Space(0)))
72+
.fold((0, 0), |(pos, checksum), b| match b {
73+
Block::File(File { id, size, .. }) => {
74+
(pos + size, checksum + id * (pos..pos + size).sum::<usize>())
75+
}
76+
Block::Space(size) => (pos + size, checksum),
77+
})
78+
.1
79+
}
80+
81+
fn next_file_to_move(&mut self) -> Option<(usize, File)> {
82+
self.iter_mut().enumerate().rev().find_map(|(i, block)| {
83+
if let Block::File(file) = block
84+
&& !file.tried_to_move
85+
{
86+
file.tried_to_move = true;
87+
Some((i, file.clone()))
88+
} else {
89+
None
90+
}
91+
})
92+
}
93+
94+
fn find_insertion_index_part2(&self, file: &File, max_index: usize) -> Option<usize> {
95+
self[..max_index].iter().enumerate().find_map(|(i, b)| {
96+
if let Block::Space(space) = b
97+
&& *space >= file.size
98+
{
99+
Some(i)
100+
} else {
101+
None
102+
}
103+
})
104+
}
105+
}
106+
107+
fn part2(mut disk: Disk) -> usize {
108+
loop {
109+
if let Some((next_file_index, file)) = disk.next_file_to_move() {
110+
if let Some(insertion_index) = disk.find_insertion_index_part2(&file, next_file_index) {
111+
*disk[insertion_index].unwrap_space_mut() -= file.size;
112+
disk[next_file_index] = Block::Space(file.size);
113+
disk.insert(insertion_index, Block::File(file));
114+
}
115+
} else {
116+
return disk.checksum();
117+
}
118+
}
119+
}
120+
121+
#[aoc_main]
122+
fn solve(input: Input) -> impl Into<Solution> {
123+
(
124+
part1(
125+
input
126+
.raw
127+
.trim()
128+
.chars()
129+
.enumerate()
130+
.flat_map(|(i, c)| {
131+
(0..(c.to_string().parse().unwrap()))
132+
.map(move |_| if i % 2 == 0 { Some(i / 2) } else { None })
133+
})
134+
.collect(),
135+
),
136+
part2(input.parse()),
137+
)
138+
}
139+
140+
#[cfg(test)]
141+
mod tests {
142+
use super::*;
143+
#[test]
144+
fn test_examples() {
145+
use utils::assert_example;
146+
assert_example!(r#"2333133121414131402"#, 1928, 2858);
147+
}
148+
}

utils/src/input.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ impl Input {
5858
self.split_and_parse(sep)
5959
}
6060

61+
pub fn parse<T: FromStr>(&self) -> T
62+
where
63+
<T as std::str::FromStr>::Err: std::fmt::Debug,
64+
{
65+
self.raw.parse().unwrap()
66+
}
67+
6168
pub fn parse_lines<T: FromStr>(&self) -> impl Iterator<Item = T> + '_ + Clone
6269
where
6370
<T as std::str::FromStr>::Err: std::fmt::Debug,

0 commit comments

Comments
 (0)