Skip to content

Commit cb7aedc

Browse files
committed
Implement 2024 day 9
1 parent dcb0f45 commit cb7aedc

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

2024/src/aoc/days/day9.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import heapq
2+
3+
from . import SeparateRunner
4+
5+
6+
def file_checksum(file_id: int, start: int, length: int) -> int:
7+
return file_id * length * (2 * start + length - 1) // 2
8+
9+
10+
class DayRunner(SeparateRunner):
11+
@classmethod
12+
def part1(cls, input: str) -> int:
13+
files = []
14+
empty = []
15+
16+
pos = 0
17+
18+
for c in input.strip():
19+
val = int(c)
20+
21+
if len(files) == len(empty):
22+
files.append((pos, val))
23+
else:
24+
empty.append((pos, val))
25+
26+
pos += val
27+
28+
checksum = 0
29+
30+
for start, length in empty:
31+
while files and length > 0:
32+
file_start, file_len = files.pop()
33+
if file_start < start:
34+
files.append((file_start, file_len))
35+
break
36+
37+
file_id = len(files)
38+
39+
infill = min(file_len, length)
40+
41+
checksum += file_checksum(file_id, start, infill)
42+
start += infill
43+
44+
if infill != file_len:
45+
files.append((file_start, file_len - infill))
46+
47+
length -= infill
48+
else:
49+
continue
50+
break
51+
52+
for file_id, (file_start, file_len) in enumerate(files):
53+
checksum += file_checksum(file_id, file_start, file_len)
54+
55+
return checksum
56+
57+
@classmethod
58+
def part2(cls, input: str) -> int:
59+
files = []
60+
empty = [[] for _ in range(10)]
61+
62+
pos = 0
63+
64+
is_file = True
65+
66+
for c in input.strip():
67+
val = int(c)
68+
69+
if is_file:
70+
files.append((pos, val))
71+
is_file = False
72+
else:
73+
# No need for heappush, as we're appending values in order
74+
empty[val].append(pos)
75+
is_file = True
76+
77+
pos += val
78+
79+
checksum = 0
80+
81+
while files:
82+
start, length = files.pop()
83+
file_id = len(files)
84+
85+
best = None
86+
best_heap = None
87+
88+
for i, heap in enumerate(empty[length:]):
89+
if not heap or heap[0] > start:
90+
continue
91+
92+
if best is None or best > heap[0]:
93+
best = heap[0]
94+
best_heap = i + length
95+
96+
if best is None:
97+
# No room to move left, count score at current position
98+
checksum += file_checksum(file_id, start, length)
99+
else:
100+
checksum += file_checksum(file_id, best, length)
101+
heapq.heappop(empty[best_heap])
102+
103+
if length < best_heap:
104+
remainder = best_heap - length
105+
heapq.heappush(empty[remainder], best + length)
106+
107+
return checksum

2024/tests/test_day09.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from aoc.days.day9 import DayRunner
2+
3+
SAMPLE = "2333133121414131402"
4+
5+
6+
def test_sample_part1() -> None:
7+
assert DayRunner.part1(SAMPLE) == 1928
8+
9+
10+
def test_sample_part2() -> None:
11+
assert DayRunner.part2(SAMPLE) == 2858

0 commit comments

Comments
 (0)