Skip to content

Commit 4aff04b

Browse files
committed
2024: Add cleaner solution to Day 9
This runs slower, but easier to understand.
1 parent 6e23e4e commit 4aff04b

File tree

1 file changed

+71
-94
lines changed

1 file changed

+71
-94
lines changed

2024/day09/src/main.rs

Lines changed: 71 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -23,137 +23,105 @@ struct Extent {
2323
length: usize,
2424
}
2525

26-
fn part1(input: &Input) -> usize {
27-
let mut free = BTreeMap::default();
28-
let mut files = BTreeMap::default();
26+
fn extents_map(extents: &[Extent]) -> BTreeMap<usize, Extent> {
27+
let mut map = BTreeMap::new();
28+
2929
let mut offset = 0;
3030

31-
for (n, len) in input.values.iter().copied().enumerate() {
32-
if len == 0 {
31+
for extent in extents {
32+
if extent.length == 0 {
3333
continue;
3434
}
3535

36-
if n % 2 == 0 {
37-
files.insert(offset, Extent {
38-
id: Some(n / 2),
39-
length: len,
40-
});
41-
} else {
42-
free.insert(offset, Extent {
43-
id: None,
44-
length: len,
45-
});
46-
}
47-
offset += len;
48-
}
36+
map.insert(offset, *extent);
4937

50-
while let Some((offset, mut hole)) = free.pop_first() {
51-
let extent = if let Some(mut entry) = files.last_entry() {
52-
if *entry.key() < offset {
53-
break;
54-
}
38+
offset += extent.length;
39+
}
5540

56-
let file = entry.get_mut();
57-
let id = file.id;
58-
let length = usize::min(hole.length, file.length);
41+
map
42+
}
5943

60-
hole.length -= length;
61-
file.length -= length;
44+
fn checksum(extents_map: &BTreeMap<usize, Extent>) -> usize {
45+
let mut checksum = 0;
6246

63-
if file.length == 0 {
64-
entry.remove();
47+
for (&offset, extent) in extents_map {
48+
if let Some(id) = extent.id {
49+
for n in offset..(offset + extent.length) {
50+
checksum += n * id;
6551
}
66-
67-
Extent { id, length }
68-
} else {
69-
panic!();
70-
};
71-
72-
files.insert(offset, extent);
73-
74-
if hole.length > 0 {
75-
free.insert(offset + extent.length, hole);
7652
}
7753
}
7854

55+
checksum
56+
}
7957

80-
let mut checksum = 0;
81-
for (offset, extent) in files {
82-
for n in offset..(offset + extent.length) {
83-
checksum += n * extent.id.unwrap();
58+
fn part1(input: &Input) -> usize {
59+
let mut extents_map = extents_map(&input.values);
60+
61+
loop {
62+
let (&hole_pos, _) = extents_map.iter().find(|(_, e)| e.id.is_none()).unwrap();
63+
let (&file_pos, _) = extents_map.iter().rfind(|(_, e)| e.id.is_some()).unwrap();
64+
65+
if file_pos <= hole_pos {
66+
// No more suitable holes left
67+
break;
8468
}
85-
}
8669

87-
checksum
88-
}
70+
let mut hole = extents_map.remove(&hole_pos).unwrap();
71+
let mut file = extents_map.remove(&file_pos).unwrap();
8972

90-
fn part2(input: &Input) -> usize {
91-
let mut free = BTreeMap::default();
92-
let mut files = BTreeMap::default();
93-
let mut offset = 0;
73+
let size = usize::min(hole.length, file.length);
74+
hole.length -= size;
75+
file.length -= size;
9476

95-
for (n, len) in input.values.iter().copied().enumerate() {
96-
if len == 0 {
97-
continue;
77+
extents_map.insert(hole_pos, Extent { id: file.id, length: size });
78+
79+
if hole.length > 0 {
80+
extents_map.insert(hole_pos + size, hole);
9881
}
9982

100-
if n % 2 == 0 {
101-
files.insert(offset, Extent {
102-
id: Some(n / 2),
103-
length: len,
104-
});
105-
} else {
106-
free.insert(offset, Extent {
107-
id: None,
108-
length: len,
109-
});
83+
if file.length > 0 {
84+
extents_map.insert(file_pos, file);
11085
}
111-
offset += len;
11286
}
11387

114-
let mut defragmented = BTreeMap::new();
115-
while let Some((cur_pos, file)) = files.pop_last() {
116-
let mut selected_hole = None;
117-
for (&hole_pos, hole) in free.iter() {
118-
if hole_pos >= cur_pos {
119-
break;
120-
}
88+
checksum(&extents_map)
89+
}
12190

122-
if file.length <= hole.length {
123-
selected_hole = Some(hole_pos);
124-
break;
91+
fn part2(input: &Input) -> usize {
92+
let mut extents_map = extents_map(&input.values);
93+
let file_positions: Vec<_> = extents_map.iter().filter_map(|(&offset, extent)| {
94+
extent.id.is_some().then_some(offset)
95+
}).collect();
96+
97+
for &file_pos in file_positions.iter().rev() {
98+
let len = extents_map[&file_pos].length;
99+
100+
if let Some((&hole_pos, _)) = extents_map.iter().find(|(_, e)| e.id.is_none() && e.length >= len) {
101+
if hole_pos >= file_pos {
102+
// No better position found
103+
continue;
125104
}
126-
}
127105

128-
if let Some(hole_pos) = selected_hole {
129-
let len = file.length;
130-
let mut hole = free.remove(&hole_pos).unwrap();
106+
let file = extents_map.remove(&file_pos).unwrap();
107+
let mut hole = extents_map.remove(&hole_pos).unwrap();
131108

132-
defragmented.insert(hole_pos, file);
133-
hole.length -= len;
109+
hole.length -= file.length;
134110

135111
if hole.length > 0 {
136-
free.insert(hole_pos + len, hole);
112+
extents_map.insert(hole_pos + file.length, hole);
137113
}
138114

139-
} else {
140-
defragmented.insert(cur_pos, file);
141-
}
142-
}
143-
144-
let mut checksum = 0;
145-
for (offset, extent) in defragmented {
146-
for n in offset..(offset + extent.length) {
147-
checksum += n * extent.id.unwrap();
115+
extents_map.insert(hole_pos, file);
148116
}
149117
}
150118

151-
checksum
119+
checksum(&extents_map)
152120
}
153121

154122
#[derive(Debug, Clone)]
155123
struct Input {
156-
values: Vec<usize>,
124+
values: Vec<Extent>,
157125
}
158126

159127
impl Input {
@@ -162,7 +130,16 @@ impl Input {
162130
let values = input
163131
.trim()
164132
.chars()
165-
.map(|c| c.to_digit(10).unwrap() as usize)
133+
.enumerate()
134+
.map(|(n, c)| {
135+
let is_file = n % 2 == 0;
136+
let length = c.to_digit(10).unwrap() as usize;
137+
138+
Extent {
139+
id: is_file.then_some(n / 2),
140+
length,
141+
}
142+
})
166143
.collect();
167144

168145
Ok(Self { values })

0 commit comments

Comments
 (0)