Implement day 9 solver
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Callable, Iterator, NamedTuple
|
|
||||||
from math import log
|
from math import log
|
||||||
|
from typing import Callable, Iterator, NamedTuple
|
||||||
|
|
||||||
test_input = """
|
test_input = """
|
||||||
190: 10 19
|
190: 10 19
|
||||||
|
138
puzzles/9.py
Normal file
138
puzzles/9.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
from itertools import islice, pairwise
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
from more_itertools import chunked, sliced
|
||||||
|
|
||||||
|
test_input = """
|
||||||
|
2333133121414131402
|
||||||
|
"""
|
||||||
|
|
||||||
|
test_solution_p1 = 1928
|
||||||
|
test_solution_p2 = 2858
|
||||||
|
|
||||||
|
|
||||||
|
def solve_p1(puzzle_input: str) -> int:
|
||||||
|
blocks = _parse_blocks(puzzle_input)
|
||||||
|
compacted_blocks = _compact_blocks(blocks)
|
||||||
|
return _hash_blocks(compacted_blocks)
|
||||||
|
|
||||||
|
|
||||||
|
def solve_p2(puzzle_input: str) -> int:
|
||||||
|
blocks = _parse_blocks(puzzle_input)
|
||||||
|
compacted_blocks = _compact_blocks_no_fragmentation(blocks)
|
||||||
|
return _hash_blocks(compacted_blocks)
|
||||||
|
|
||||||
|
|
||||||
|
class Block(NamedTuple):
|
||||||
|
id: int
|
||||||
|
position: int
|
||||||
|
size: int
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_blocks(puzzle_input: str) -> list[Block]:
|
||||||
|
blocks: list[Block] = []
|
||||||
|
cursor_position = 0
|
||||||
|
current_id = 0
|
||||||
|
|
||||||
|
snapshot = map(int, puzzle_input.strip())
|
||||||
|
for pair in chunked(snapshot, 2):
|
||||||
|
if data_size := pair[0]:
|
||||||
|
blocks.append(Block(current_id, cursor_position, data_size))
|
||||||
|
cursor_position += data_size
|
||||||
|
current_id += 1
|
||||||
|
if len(pair) == 2:
|
||||||
|
cursor_position += pair[1]
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
|
def _compact_blocks(blocks: list[Block]) -> list[Block]:
|
||||||
|
assert blocks
|
||||||
|
blocks = blocks.copy()
|
||||||
|
|
||||||
|
compacted_blocks: list[Block] = []
|
||||||
|
current_position = 0
|
||||||
|
moving_block = blocks.pop()
|
||||||
|
|
||||||
|
while blocks:
|
||||||
|
next_block = blocks.pop(0)
|
||||||
|
free_space = next_block.position - current_position
|
||||||
|
while free_space:
|
||||||
|
assert moving_block.size > 0
|
||||||
|
allocation = min(free_space, moving_block.size)
|
||||||
|
compacted_blocks.append(
|
||||||
|
Block(
|
||||||
|
moving_block.id,
|
||||||
|
current_position,
|
||||||
|
allocation,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
current_position += allocation
|
||||||
|
free_space -= allocation
|
||||||
|
if moving_block.size > allocation:
|
||||||
|
moving_block = Block(
|
||||||
|
moving_block.id,
|
||||||
|
moving_block.position + allocation,
|
||||||
|
moving_block.size - allocation,
|
||||||
|
)
|
||||||
|
elif blocks:
|
||||||
|
moving_block = blocks.pop()
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
compacted_blocks.append(next_block)
|
||||||
|
current_position += next_block.size
|
||||||
|
|
||||||
|
compacted_blocks.append(
|
||||||
|
Block(
|
||||||
|
moving_block.id,
|
||||||
|
current_position,
|
||||||
|
moving_block.size,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return compacted_blocks
|
||||||
|
|
||||||
|
|
||||||
|
def _compact_blocks_no_fragmentation(blocks: list[Block]) -> list[Block]:
|
||||||
|
assert blocks
|
||||||
|
blocks = blocks.copy()
|
||||||
|
|
||||||
|
compacted_blocks: list[Block] = []
|
||||||
|
|
||||||
|
while len(blocks) > 1:
|
||||||
|
moving_block = blocks[-1]
|
||||||
|
for (_, a), (i, b) in pairwise(enumerate(blocks)):
|
||||||
|
free_space = b.position - a.position - a.size
|
||||||
|
if free_space >= moving_block.size:
|
||||||
|
modified_block = Block(
|
||||||
|
moving_block.id,
|
||||||
|
a.position + a.size,
|
||||||
|
moving_block.size,
|
||||||
|
)
|
||||||
|
blocks.pop()
|
||||||
|
blocks.insert(i, modified_block)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
blocks.pop()
|
||||||
|
compacted_blocks.append(moving_block)
|
||||||
|
|
||||||
|
compacted_blocks.append(blocks[0])
|
||||||
|
|
||||||
|
return list(sorted(compacted_blocks, key=lambda block: block.position))
|
||||||
|
|
||||||
|
|
||||||
|
def _print_blocks(blocks: list[Block]) -> str:
|
||||||
|
snapshot = []
|
||||||
|
for a, b in pairwise(blocks):
|
||||||
|
snapshot.append(str(a.id) * a.size)
|
||||||
|
snapshot.append("." * (b.position - a.position - a.size))
|
||||||
|
snapshot.append(str(blocks[-1].id) * blocks[-1].size)
|
||||||
|
|
||||||
|
|
||||||
|
def _hash_blocks(blocks: list[Block]) -> int:
|
||||||
|
return sum(
|
||||||
|
block.id * pos
|
||||||
|
for block in blocks
|
||||||
|
for pos in range(block.position, block.position + block.size)
|
||||||
|
)
|
Reference in New Issue
Block a user