Skip to content

Commit 1dee56a

Browse files
committed
Day 9
1 parent c4781c6 commit 1dee56a

File tree

3 files changed

+220
-0
lines changed

3 files changed

+220
-0
lines changed

data/examples/09-1.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
12345

data/examples/09-2.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2333133121414131402

src/bin/09.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
use std::fmt::Debug;
2+
3+
advent_of_code::solution!(9);
4+
5+
#[derive(Debug, Clone)]
6+
struct Block {
7+
length: u32,
8+
file_id: Option<u32>,
9+
}
10+
11+
impl Block {
12+
fn is_file(&self) -> bool {
13+
self.file_id.is_some()
14+
}
15+
16+
fn is_free(&self) -> bool {
17+
self.file_id.is_none()
18+
}
19+
20+
fn split(&self, length: u32) -> (Block, Option<Block>) {
21+
if self.length <= length {
22+
(self.clone(), None)
23+
} else {
24+
(
25+
Block {
26+
length: length,
27+
file_id: self.file_id,
28+
},
29+
Some(Block {
30+
length: self.length - length,
31+
file_id: self.file_id,
32+
}),
33+
)
34+
}
35+
}
36+
}
37+
38+
struct BlocksFmt<'a>(&'a Vec<Block>);
39+
40+
impl Debug for BlocksFmt<'_> {
41+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42+
for block in self.0 {
43+
for _ in 0..block.length {
44+
match block.file_id {
45+
Some(id) => write!(f, "{}", id)?,
46+
None => write!(f, ".")?,
47+
}
48+
}
49+
}
50+
Ok(())
51+
}
52+
}
53+
54+
fn checksum(blocks: &Vec<Block>) -> u64 {
55+
let mut sum: u64 = 0;
56+
let mut idx = 0;
57+
for block in blocks {
58+
for _ in 0..block.length {
59+
if let Some(file_id) = block.file_id {
60+
sum += idx * (file_id as u64);
61+
}
62+
idx += 1;
63+
}
64+
}
65+
sum
66+
}
67+
68+
fn parse_blocks(input: &str) -> Vec<Block> {
69+
let nums = input
70+
.chars()
71+
.map(|c| c.to_digit(10))
72+
.filter(|r| r.is_some())
73+
.map(|r| r.unwrap());
74+
75+
let mut blocks = Vec::with_capacity(1000);
76+
for (i, num) in nums.enumerate() {
77+
let file_id = if i % 2 == 0 {
78+
Some((i / 2) as u32)
79+
} else {
80+
None
81+
};
82+
let b = Block {
83+
file_id,
84+
length: num,
85+
};
86+
87+
blocks.push(b);
88+
}
89+
90+
blocks
91+
}
92+
93+
pub fn part_one(input: &str) -> Option<u64> {
94+
let mut blocks = parse_blocks(input);
95+
96+
// have 2 pointers, one at the start of the disk, one at the end
97+
// move the pointer at the end to the leftmost free space block
98+
// move the pointer at the start to the rightmost file block
99+
// if the pointers meet, we're done
100+
// if we have a free space block, we want to grab some of the file blocks to put in place of it
101+
102+
let mut left_block_idx = 0;
103+
let mut right_block_idx = blocks.len() - 1;
104+
while left_block_idx < right_block_idx {
105+
let left_block = &blocks[left_block_idx];
106+
let right_block = &blocks[right_block_idx];
107+
108+
if !left_block.is_free() {
109+
left_block_idx += 1;
110+
continue;
111+
}
112+
113+
if !right_block.is_file() {
114+
right_block_idx -= 1;
115+
continue;
116+
}
117+
118+
// now we have a free space block and a file block
119+
// we want to grab some of the file blocks to put in place of the free space block
120+
121+
let left_size = left_block.length;
122+
let (right_movable, maybe_right_unmovable) = right_block.split(left_size);
123+
let (_, maybe_left_additional_space) = left_block.split(right_movable.length);
124+
125+
// move the movable part
126+
if let Some(left_additional_space) = maybe_left_additional_space {
127+
blocks.insert(left_block_idx, right_movable);
128+
blocks[left_block_idx + 1] = left_additional_space;
129+
right_block_idx += 1;
130+
} else {
131+
blocks[left_block_idx] = right_movable;
132+
}
133+
134+
if let Some(right_unmovable) = maybe_right_unmovable {
135+
blocks[right_block_idx] = right_unmovable;
136+
} else {
137+
blocks.remove(right_block_idx);
138+
right_block_idx -= 1;
139+
}
140+
}
141+
142+
Some(checksum(&blocks))
143+
}
144+
145+
pub fn part_two(input: &str) -> Option<u64> {
146+
let mut blocks = parse_blocks(input);
147+
148+
// 2 pointers again but this time we want to move whole files to the leftmost span of free space blocks that could fit the file
149+
// we want to move the files in order of decreasing file ID number
150+
// if there is no span of free space to the left of a file that is large enough to fit the file, the file does not move
151+
let mut right_block_idx = blocks.len() - 1;
152+
while right_block_idx > 0 {
153+
let right_block = &blocks[right_block_idx];
154+
if right_block.is_free() {
155+
right_block_idx -= 1;
156+
continue;
157+
}
158+
159+
// look for a free space block that can fit the file
160+
let mut free_space_block_idx = None;
161+
for i in 0..right_block_idx {
162+
let left_block = &blocks[i];
163+
if left_block.is_free() && left_block.length >= right_block.length {
164+
free_space_block_idx = Some(i);
165+
break;
166+
}
167+
}
168+
169+
if let Some(left_block_idx) = free_space_block_idx {
170+
let left_block = &blocks[left_block_idx];
171+
172+
let (_, maybe_left_additional_space) = left_block.split(right_block.length);
173+
if let Some(left_additional_space) = maybe_left_additional_space {
174+
blocks.insert(left_block_idx, right_block.clone());
175+
blocks[left_block_idx + 1] = left_additional_space;
176+
right_block_idx += 1;
177+
} else {
178+
blocks[left_block_idx] = right_block.clone();
179+
}
180+
181+
blocks[right_block_idx].file_id = None;
182+
right_block_idx -= 1;
183+
}
184+
185+
right_block_idx -= 1;
186+
}
187+
188+
Some(checksum(&blocks))
189+
}
190+
191+
#[cfg(test)]
192+
mod tests {
193+
use super::*;
194+
195+
#[test]
196+
fn test_part_one_1() {
197+
let result = part_one(&advent_of_code::template::read_file_part(
198+
"examples", DAY, 1,
199+
));
200+
assert_eq!(result, Some(60));
201+
}
202+
203+
#[test]
204+
fn test_part_one_2() {
205+
let result = part_one(&advent_of_code::template::read_file_part(
206+
"examples", DAY, 2,
207+
));
208+
assert_eq!(result, Some(1928));
209+
}
210+
211+
#[test]
212+
fn test_part_two_2() {
213+
let result = part_two(&advent_of_code::template::read_file_part(
214+
"examples", DAY, 2,
215+
));
216+
assert_eq!(result, Some(2858));
217+
}
218+
}

0 commit comments

Comments
 (0)