Skip to content

Commit

Permalink
[merge] vLabayen_2023_d5
Browse files Browse the repository at this point in the history
  • Loading branch information
vLabayen committed Dec 5, 2023
2 parents b252b7b + 2b7a4b4 commit 87691bd
Show file tree
Hide file tree
Showing 5 changed files with 403 additions and 0 deletions.
2 changes: 2 additions & 0 deletions vLabayen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/__pycache__
**/input.txt
72 changes: 72 additions & 0 deletions vLabayen/2023/d5/d5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
from typing import Generator, List
import re
from data_models import Map, Transformation, Range


parse_seeds_rgx = re.compile('[0-9]+')
def read_seeds(lines: Generator[str, None, None]) -> List[int]:
return [int(seed) for seed in parse_seeds_rgx.findall(next(lines).split(':')[1])]


def seeds_as_ranges(seeds: List[int]) -> List[Range]:
pairs_gen = (seeds[2*i:2*(i+1)] for i in range(len(seeds) // 2))
return [Range(start, start + size) for start, size in pairs_gen]

parse_range_rgx = re.compile('([0-9]+) ([0-9]+) ([0-9]+)')
def read_map(lines: Generator[str, None, None]) -> Map:
next(lines)

ranges = []
current_range = next(lines)
while current_range != '':
dst_start, src_start, range_len = (int(n) for n in parse_range_rgx.match(current_range).groups()) # type: ignore
ranges.append(Transformation(
src_range = Range(src_start, src_start + range_len),
dst_range = Range(dst_start, dst_start + range_len),
))

try: current_range = next(lines)
except StopIteration: break

return Map(ranges)

def read_file(file: str):
with open(file, 'r') as f:
lines = (l.strip() for l in f)
seeds = read_seeds(lines)

next(lines)
maps = [read_map(lines) for _ in range(7)]

return seeds, maps

def p1(args):
seeds, maps = read_file(args.file)
map = Map.merge_maps(maps)

print(min(map.transform(seed) for seed in seeds))

def p2(args):
seeds, maps = read_file(args.file)
map = Map.merge_maps(maps)

sorted_transformations = sorted(map.transformations, key=lambda t: t.dst_range.start)
for transform in sorted_transformations:
for seed_range in seeds_as_ranges(seeds):
_, union, _ = transform.src_range.merge(seed_range)
if union is not None:
print(union.start + transform.add_src2dst)
return

if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', type=str, default='input.txt')
parser.add_argument('-v', '--verbose', type=str, choices={'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}, default='WARNING')
args = parser.parse_args()

logging.basicConfig(level=args.verbose)

p1(args)
p2(args)
109 changes: 109 additions & 0 deletions vLabayen/2023/d5/data_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from typing import List, Tuple, Optional
from attrs import define, field
from functools import reduce

@define
class Range:
''' Range of numbers: [start, end) '''
start: int
end : int

def shift(self, n: int) -> 'Range': return Range(
start = self.start + n,
end = self.end + n
)

def merge(self, other: 'Range') -> Tuple[Optional['Range'], Optional['Range'], Optional['Range']]:
result: List[Optional[Range]] = [None, None, None]

# Difference before the start
if other.start < self.start:
result[0] = Range(other.start, min(other.end, self.start))

# Intersecction
if not (self.end <= other.start or other.end <= self.start):
result[1] = Range(max(self.start, other.start), min(self.end, other.end))

# Difference after the end
if self.end < other.end:
result[2] = Range(max(other.start, self.end), other.end)

return tuple(result)

@define
class Transformation:
src_range: Range
dst_range: Range

add_src2dst: int = field(init=False, repr=False)
add_dst2src: int = field(init=False, repr=False)
def __attrs_post_init__(self):
self.add_src2dst = self.dst_range.start - self.src_range.start
self.add_dst2src = -self.add_src2dst

def match(self, n: int) -> bool:
return self.src_range.start <= n < self.src_range.end

@define
class Map:
transformations: List[Transformation]

@staticmethod
def merge(input: 'Map', output: 'Map') -> 'Map':
merged_transformations = []

# Compute merged transformations and input-unmatched transformations
input_transforms: List[Transformation] = [transform for transform in input.transformations]
for out_transform in output.transformations:
unmatched: List[Transformation] = []
for in_transform in input_transforms:
left, union, right = out_transform.src_range.merge(in_transform.dst_range)

if left is not None: unmatched.append(Transformation(
src_range = left.shift(in_transform.add_dst2src),
dst_range = left
))

if right is not None: unmatched.append(Transformation(
src_range = right.shift(in_transform.add_dst2src),
dst_range = right
))

if union is not None: merged_transformations.append(Transformation(
src_range = union.shift(in_transform.add_dst2src),
dst_range = union.shift(out_transform.add_src2dst)
))

# Continue with the unmatched ones for the next output
input_transforms = unmatched

# Compute output-unmatched transformations
output_transforms: List[Transformation] = [transform for transform in output.transformations]
for in_transform in input.transformations:
unmatched: List[Transformation] = []
for out_transform in output_transforms:
left, _, right = in_transform.dst_range.merge(out_transform.src_range)

if left is not None: unmatched.append(Transformation(
src_range = left,
dst_range = left.shift(out_transform.add_src2dst)
))

if right is not None: unmatched.append(Transformation(
src_range = right,
dst_range = right.shift(out_transform.add_src2dst)
))

output_transforms = unmatched

return Map(merged_transformations + input_transforms + output_transforms)

@staticmethod
def merge_maps(maps: List['Map']):
return reduce(lambda current, next: Map.merge(current, next), maps[1:], maps[0])

def transform(self, n: int) -> int:
for transform in self.transformations:
if transform.match(n): return n + transform.add_src2dst

return n
33 changes: 33 additions & 0 deletions vLabayen/2023/d5/example.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
seeds: 79 14 55 13

seed-to-soil map:
50 98 2
52 50 48

soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15

fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4

water-to-light map:
88 18 7
18 25 70

light-to-temperature map:
45 77 23
81 45 19
68 64 13

temperature-to-humidity map:
0 69 1
1 0 69

humidity-to-location map:
60 56 37
56 93 4
Loading

0 comments on commit 87691bd

Please sign in to comment.