From 422a378ec06d1c06909a342efa6964cc828e425e Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Mon, 4 Dec 2023 23:08:03 -0800 Subject: [PATCH] 2023 Day 5 Part 2 --- 2023/day05.nim | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/2023/day05.nim b/2023/day05.nim index 0a275e3..31f48da 100644 --- a/2023/day05.nim +++ b/2023/day05.nim @@ -44,8 +44,8 @@ func lookup(m: Map, src: int): int = return remap.lookup(src) src -type Almanac = tuple - seeds: HashSet[int] +type Almanac = object + seeds: seq[int] mapsBySrc: Table[string, Map] mapsByDst: Table[string, Map] @@ -57,7 +57,7 @@ func parseAlmanac(s: string): Almanac = raise newException(ValueError, sections[0]) for num in seeds.strip.splitWhitespace: - result.seeds.incl num.parseInt + result.seeds.add num.parseInt for section in sections[1..^1]: let map = parseMap(section) @@ -85,4 +85,89 @@ proc part1(): int = locations.min +type Range = tuple + lo: int + hi: int + +func newRange(start: int, length: int): Range = + (start, start + length - 1) + +func empty(r: Range): bool = + r.lo > r.hi + +func intersect(a: Range, b: Range): Range = + (max(a.lo, b.lo), min(a.hi, b.hi)) + +proc lookup(r: Remap, src: Range): Range = + let delta = r.dst_start - r.src_start + (src.lo + delta, src.hi + delta) + +proc lookup(m: Map, src: Range): seq[Range] = + var unmapped = @[src] + + for remap in m.remaps: + var still_unmapped: seq[Range] + for src in unmapped: + let rr = newRange(remap.src_start, remap.length) + if src.intersect(rr).empty: + still_unmapped.add src + continue + + let dn = src.lo + let lo = max(src.lo, rr.lo) + let hi = min(src.hi, rr.hi) + let up = src.hi + + let prefix = (dn, lo - 1) + let inside = (lo, hi) + let suffix = (hi + 1, up) + + if not prefix.empty: + still_unmapped.add prefix + + if not suffix.empty: + still_unmapped.add suffix + + if not inside.empty: + result.add remap.lookup(inside) + + unmapped = still_unmapped + + for src in unmapped: + result.add src + +proc lookup(m: Map, srcs: seq[Range]): seq[Range] = + for src in srcs: + result.add m.lookup(src) + +proc lookup(a: Almanac, dst: string, seeds: seq[Range]): seq[Range] = + var map = a.mapsBySrc["seed"] + var src = seeds + + while true: + src = map.lookup(src) + if map.dst == dst: + return src + + map = a.mapsBySrc[map.dst] + +proc part2(): int = + let text = readFile("input/day05.txt") + let almanac = parseAlmanac(text) + + var ranges: seq[Range] + + for i in 0 ..< floorDiv(almanac.seeds.len, 2): + let start = almanac.seeds[2*i] + let len = almanac.seeds[2*i + 1] + ranges.add newRange(start, len) + + let locations = collect(newSeq): + for locs in almanac.lookup("location", ranges): + if not locs.empty: + locs.lo + + locations.min + echo part1() +echo part2()