Skip to content

Commit 0959f19

Browse files
committed
feat(09/2024): solve second part
1 parent f6a88cf commit 0959f19

File tree

2 files changed

+190
-11
lines changed

2 files changed

+190
-11
lines changed

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
| [Day 6: Guard Gallivant](src/solutions/year2024/day06.rs) | ⭐⭐ | 8.738 | 3375.211 |
2020
| [Day 7: Bridge Repair](src/solutions/year2024/day07.rs) | ⭐⭐ | 1.198 | 219.754 |
2121
| [Day 8: Resonant Collinearity](src/solutions/year2024/day08.rs) | ⭐⭐ | 0.883 | 1.238 |
22-
| [Day 9: Disk Fragmenter](src/solutions/year2024/day09.rs) | | 387.168 | - |
22+
| [Day 9: Disk Fragmenter](src/solutions/year2024/day09.rs) | | 387.168 | 658.58 |
2323

2424
# 2023
2525

src/solutions/year2024/day09.rs

Lines changed: 189 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,55 @@ impl Solution for Day09 {
2323
disk_map.checksum().to_string()
2424
}
2525

26-
fn part_two(&self, _input: &str) -> String {
27-
String::from('0')
26+
fn part_two(&self, input: &str) -> String {
27+
let mut block_disk_map = BlockDiskMap::from_str(input).unwrap();
28+
let mut last_checked_index = usize::MAX;
29+
30+
loop {
31+
let cloned = block_disk_map.blocks.clone();
32+
let last_filled_block = cloned.iter().enumerate().rfind(|(i, block)| {
33+
last_checked_index > *i && matches!(block, Block::Filled { .. })
34+
});
35+
36+
if last_filled_block.is_none() {
37+
break;
38+
}
39+
40+
let filled_unwrapped = last_filled_block.unwrap();
41+
last_checked_index = filled_unwrapped.0;
42+
43+
let cloned = block_disk_map.blocks.clone();
44+
let first_matching_spot =
45+
cloned
46+
.iter()
47+
.enumerate()
48+
.find(|(empty_index, block)| match block {
49+
Block::Empty { size } => {
50+
empty_index < &filled_unwrapped.0 && size >= &filled_unwrapped.1.size()
51+
}
52+
_ => false,
53+
});
54+
55+
if first_matching_spot.is_none() {
56+
continue;
57+
}
58+
59+
let (empty_index, matching_block) = first_matching_spot.unwrap();
60+
let (filled_index, filled_block) = last_filled_block.unwrap();
61+
62+
let split = matching_block.split(filled_block);
63+
64+
block_disk_map.blocks[filled_index] = Block::Empty {
65+
size: filled_block.size(),
66+
};
67+
block_disk_map.blocks.remove(empty_index);
68+
69+
for (i, block) in split.iter().enumerate() {
70+
block_disk_map.blocks.insert(empty_index + i, block.clone());
71+
}
72+
}
73+
74+
Into::<DiskMap>::into(block_disk_map).checksum().to_string()
2875
}
2976
}
3077

@@ -36,20 +83,25 @@ impl DiskMap {
3683
fn checksum(&self) -> usize {
3784
self.blocks
3885
.clone()
39-
.into_iter()
40-
.flatten()
86+
.iter()
4187
.enumerate()
42-
.fold(0, |acc, (i, id)| acc + i * id)
88+
.fold(0, |acc, (i, id)| {
89+
if let Some(id) = id {
90+
return acc + i * id;
91+
}
92+
93+
acc
94+
})
4395
}
4496
}
4597

4698
impl FromStr for DiskMap {
47-
type Err = String;
99+
type Err = ();
48100

49101
fn from_str(s: &str) -> Result<Self, Self::Err> {
50102
let mut current_id = 0;
51103

52-
let test: Vec<Option<usize>> = s
104+
let blocks: Vec<Option<usize>> = s
53105
.trim()
54106
.chars()
55107
.enumerate()
@@ -74,7 +126,7 @@ impl FromStr for DiskMap {
74126
})
75127
.collect();
76128

77-
Ok(Self { blocks: test })
129+
Ok(Self { blocks })
78130
}
79131
}
80132

@@ -93,9 +145,115 @@ impl Display for DiskMap {
93145
}
94146
}
95147

148+
impl From<BlockDiskMap> for DiskMap {
149+
fn from(value: BlockDiskMap) -> Self {
150+
let blocks = value
151+
.blocks
152+
.into_iter()
153+
.flat_map(|v| match v {
154+
Block::Empty { size } => vec![None; size],
155+
Block::Filled { size, id } => vec![Some(id); size],
156+
})
157+
.collect();
158+
159+
Self { blocks }
160+
}
161+
}
162+
163+
#[derive(Debug, PartialEq, Clone)]
164+
enum Block {
165+
Empty { size: usize },
166+
Filled { size: usize, id: usize },
167+
}
168+
169+
impl Block {
170+
fn size(&self) -> usize {
171+
match self {
172+
Block::Empty { size } => size.to_owned(),
173+
Block::Filled { size, .. } => size.to_owned(),
174+
}
175+
}
176+
177+
fn split(&self, other: &Self) -> Vec<Self> {
178+
match (self, other) {
179+
(
180+
Block::Empty { size },
181+
Block::Filled {
182+
size: filled_size,
183+
id,
184+
},
185+
) => {
186+
if filled_size > size {
187+
panic!("filled size cannot be greater than empty");
188+
}
189+
190+
if filled_size == size {
191+
return vec![Block::Filled {
192+
size: *filled_size,
193+
id: *id,
194+
}];
195+
}
196+
197+
if filled_size < size {
198+
return vec![
199+
Block::Filled {
200+
size: *filled_size,
201+
id: *id,
202+
},
203+
Block::Empty {
204+
size: size - filled_size,
205+
},
206+
];
207+
}
208+
209+
unreachable!()
210+
}
211+
(_, _) => panic!("illegal block"),
212+
}
213+
}
214+
}
215+
216+
#[derive(Debug, PartialEq, Clone)]
217+
struct BlockDiskMap {
218+
blocks: Vec<Block>,
219+
}
220+
221+
impl FromStr for BlockDiskMap {
222+
type Err = ();
223+
224+
fn from_str(s: &str) -> Result<Self, Self::Err> {
225+
let mut current_id = 0;
226+
227+
let blocks: Vec<Block> = s
228+
.trim()
229+
.chars()
230+
.enumerate()
231+
.map(|(i, c)| {
232+
let size: usize = c
233+
.to_string()
234+
.parse()
235+
.unwrap_or_else(|_| panic!("cannot parse char to usize: '{}'", c));
236+
237+
match i % 2 == 0 {
238+
true => {
239+
let id = current_id;
240+
241+
current_id += 1;
242+
243+
Block::Filled { size, id }
244+
}
245+
false => Block::Empty { size },
246+
}
247+
})
248+
.collect();
249+
250+
Ok(Self { blocks })
251+
}
252+
}
253+
96254
#[cfg(test)]
97255
mod tests {
98-
use crate::solutions::year2024::day09::{Day09, DiskMap};
256+
use crate::solutions::year2024::day09::{Block, BlockDiskMap, Day09, DiskMap};
99257
use crate::solutions::Solution;
100258
use std::str::FromStr;
101259

@@ -107,7 +265,12 @@ mod tests {
107265
}
108266

109267
#[test]
110-
fn parse_test() {
268+
fn part_two_example_test() {
269+
assert_eq!("2858", Day09.part_two(EXAMPLE));
270+
}
271+
272+
#[test]
273+
fn disk_map_parse_test() {
111274
let result = DiskMap::from_str("12345").unwrap();
112275
assert_eq!("0..111....22222", result.to_string());
113276

@@ -117,4 +280,20 @@ mod tests {
117280
result.to_string()
118281
);
119282
}
283+
284+
#[test]
285+
fn block_disk_map_parse_test() {
286+
let sut = BlockDiskMap::from_str("12345").unwrap();
287+
let expected = BlockDiskMap {
288+
blocks: vec![
289+
Block::Filled { size: 1, id: 0 },
290+
Block::Empty { size: 2 },
291+
Block::Filled { size: 3, id: 1 },
292+
Block::Empty { size: 4 },
293+
Block::Filled { size: 5, id: 2 },
294+
],
295+
};
296+
297+
assert_eq!(expected, sut);
298+
}
120299
}

0 commit comments

Comments
 (0)