113 lines
2.7 KiB
Python
113 lines
2.7 KiB
Python
|
import shared
|
||
|
from pprint import pprint
|
||
|
from dataclasses import dataclass
|
||
|
|
||
|
@dataclass
|
||
|
class Mapping:
|
||
|
dst: int
|
||
|
src: int
|
||
|
ran: int # RANGE
|
||
|
|
||
|
|
||
|
maps_parts = [
|
||
|
"seed-to-soil",
|
||
|
"soil-to-fertilizer",
|
||
|
"fertilizer-to-water",
|
||
|
"water-to-light",
|
||
|
"light-to-temperature",
|
||
|
"temperature-to-humidity",
|
||
|
"humidity-to-location",
|
||
|
]
|
||
|
|
||
|
group = lambda l,s: list(zip(*(iter(l),) * s))
|
||
|
|
||
|
def line_ints(s):
|
||
|
return [int(x) for x in s.split() if s]
|
||
|
|
||
|
def parse_line(line):
|
||
|
return Mapping(*line)
|
||
|
|
||
|
class Mapper:
|
||
|
def __init__(self, rows):
|
||
|
self.parse(rows)
|
||
|
|
||
|
def parse(self, rows):
|
||
|
seeds = rows.pop(0)
|
||
|
rows.pop(0) # Discard empty to start at a clean slate
|
||
|
seeds = line_ints(seeds.split(":")[1])
|
||
|
maps = {}
|
||
|
current_map = None
|
||
|
for row in rows:
|
||
|
if ":" in row:
|
||
|
current_map = row[:-5]
|
||
|
maps[current_map] = []
|
||
|
continue
|
||
|
if row == "":
|
||
|
continue
|
||
|
maps[current_map].append(parse_line(line_ints(row)))
|
||
|
self.seeds = seeds
|
||
|
self.maps = maps
|
||
|
|
||
|
def calculate(self, step, source):
|
||
|
possibilities = self.maps[step]
|
||
|
for p in possibilities:
|
||
|
if source < p.src:
|
||
|
#print(f"\t{source} less than {p.src}")
|
||
|
continue
|
||
|
if source > p.src+ p.ran:
|
||
|
#print(f"\t{source} greater than {p.src}+{p.ran}={p.src+p.ran}")
|
||
|
continue
|
||
|
#print(f"\t{source} in range")
|
||
|
#print(f"\t{source} is {p.dst + (source - p.src)}")
|
||
|
return p.dst + (source - p.src)
|
||
|
break
|
||
|
return source
|
||
|
|
||
|
# @shared.profile
|
||
|
def part1(rows):
|
||
|
mapper = Mapper(rows[:])
|
||
|
locations = []
|
||
|
|
||
|
for seed in mapper.seeds:
|
||
|
next = seed
|
||
|
for part in maps_parts:
|
||
|
_part = next
|
||
|
next = mapper.calculate(part, next)
|
||
|
locations.append(next)
|
||
|
print(locations)
|
||
|
print(min(locations))
|
||
|
|
||
|
|
||
|
|
||
|
# @shared.profile
|
||
|
def part2(rows):
|
||
|
mapper = Mapper(rows[:])
|
||
|
locations = []
|
||
|
seeds = group(mapper.seeds, 2)
|
||
|
|
||
|
|
||
|
for seed_group in seeds:
|
||
|
for seed in range(seed_group[0], seed_group[0]+seed_group[1]):
|
||
|
next = seed
|
||
|
for part in maps_parts:
|
||
|
_part = next
|
||
|
next = mapper.calculate(part, next)
|
||
|
locations.append(next)
|
||
|
print(min(locations))
|
||
|
|
||
|
|
||
|
|
||
|
def main():
|
||
|
rows = [row for row in shared.load_rows(5)]
|
||
|
with shared.elapsed_timer() as elapsed:
|
||
|
part1(rows)
|
||
|
print("🕒", elapsed())
|
||
|
|
||
|
with shared.elapsed_timer() as elapsed:
|
||
|
part2(rows)
|
||
|
print("🕒", elapsed())
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|