diff options
Diffstat (limited to 'aoc2023/src/day5/solve.gleam')
-rw-r--r-- | aoc2023/src/day5/solve.gleam | 112 |
1 files changed, 55 insertions, 57 deletions
diff --git a/aoc2023/src/day5/solve.gleam b/aoc2023/src/day5/solve.gleam index aba405d..7a0fe33 100644 --- a/aoc2023/src/day5/solve.gleam +++ b/aoc2023/src/day5/solve.gleam @@ -6,11 +6,13 @@ import gleam/list.{Continue, Stop} import gleam/int import gleam/function +// Types ------------------------------------------------------------------------------------------- + pub type Almanac { Almanac(seeds: List(Int), mappers: List(Mapper)) } -pub type MRange { +pub type MappingRange { MRange(start: Int, end: Int, offset: Int) } @@ -19,14 +21,9 @@ pub type SeedRange { } type Mapper = - List(MRange) + List(MappingRange) -fn string_to_int_list(str: String) { - str - |> string.split(on: " ") - |> list.map(int.parse) - |> result.values -} +// Parsing ----------------------------------------------------------------------------------------- fn parse_input(input: String) { let ["seeds: " <> raw_seeds, ..raw_mappers] = string.split(input, on: "\n\n") @@ -40,23 +37,33 @@ fn parse_input(input: String) { Almanac(seeds, mappers) } +fn string_to_int_list(str: String) { + str + |> string.split(on: " ") + |> list.map(int.parse) + |> result.values +} + fn parse_mapper(strs: List(String)) -> Mapper { let [_, ..raw_ranges] = strs list.map(raw_ranges, parse_mrange) - |> list.sort(compare_mranges) + |> list.sort(fn(a, b) { int.compare(a.start, b.start) }) } -fn parse_mrange(str: String) -> MRange { +fn parse_mrange(str: String) -> MappingRange { let assert [destination, source, range_width] = string_to_int_list(str) MRange(source, source + range_width - 1, destination - source) } -fn compare_mranges(mrange1: MRange, mrange2: MRange) { - int.compare(mrange1.start, mrange2.start) -} +// Part 1 ------------------------------------------------------------------------------------------ -fn is_in_mrange(n: Int, mrange: MRange) { - mrange.start <= n && n <= mrange.end +pub fn part1(input: String) { + let Almanac(seeds, mappers) = parse_input(input) + + list.map(seeds, list.fold(over: mappers, from: _, with: correspond)) + |> list.reduce(int.min) + |> result.unwrap(0) + |> string.inspect } fn correspond(n: Int, mapper: Mapper) { @@ -64,7 +71,7 @@ fn correspond(n: Int, mapper: Mapper) { over: mapper, from: n, with: fn(acc, mrange) { - case is_in_mrange(acc, mrange) { + case mrange.start <= acc && acc <= mrange.end { True -> Stop(acc + mrange.offset) False -> Continue(acc) } @@ -72,19 +79,42 @@ fn correspond(n: Int, mapper: Mapper) { ) } -pub fn part1(input: String) { +// Part 2 ------------------------------------------------------------------------------------------ + +pub fn part2(input: String) { let Almanac(seeds, mappers) = parse_input(input) - list.map(seeds, list.fold(over: mappers, from: _, with: correspond)) - |> list.reduce(int.min) - |> result.unwrap(0) - |> string.inspect + let [SRange(answer, _), ..] = + seeds + |> list.sized_chunk(into: 2) + |> list.map(fn(chunk) { + let [start, length] = chunk + [SRange(start, start + length - 1)] + |> remap_all_seed_ranges(mappers) + }) + |> list.flatten() + |> list.sort(fn(a, b) { int.compare(a.start, b.start) }) + + string.inspect(answer) +} + +fn remap_all_seed_ranges(srs: List(SeedRange), mappers: List(Mapper)) { + case mappers { + [] -> srs + [mapper, ..rest] -> + list.flat_map(srs, remap_range(_, mapper)) + |> remap_all_seed_ranges(rest) + } } fn remap_range(r: SeedRange, mapper: Mapper) -> List(SeedRange) { do_remap_range(r, mapper, []) } +fn transform_range(r: SeedRange, mapper: MappingRange) -> SeedRange { + SRange(r.start + mapper.offset, r.end + mapper.offset) +} + fn do_remap_range(r: SeedRange, mapper: Mapper, acc: List(SeedRange)) { case mapper { // no more mappings -> no mapping covers this range @@ -95,13 +125,13 @@ fn do_remap_range(r: SeedRange, mapper: Mapper, acc: List(SeedRange)) { [m, ..ms] if r.start > m.end -> do_remap_range(r, ms, acc) // range is fully inside mapping -> range is transformed [m, ..] if r.start >= m.start && r.end <= m.end -> [ - SRange(r.start + m.offset, r.end + m.offset), + transform_range(r, m), ..acc ] // range overlaps start but not end -> left side not transformed, right side transformed [m, ..] if r.start < m.start && r.end <= m.end -> [ SRange(r.start, m.start - 1), - SRange(m.start + m.offset, r.end + m.offset), + transform_range(SRange(m.start, r.end), m), ..acc ] // range overlaps end but not start -> left side transformed, right side moves to next mapping @@ -109,7 +139,7 @@ fn do_remap_range(r: SeedRange, mapper: Mapper, acc: List(SeedRange)) { do_remap_range( SRange(m.end + 1, r.end), ms, - [SRange(r.start + m.offset, m.end + m.offset), ..acc], + [transform_range(SRange(r.start, m.end), m), ..acc], ) // mapping is fully inside range -> left not transformed, middle transformed, right to next [m, ..ms] -> @@ -118,45 +148,13 @@ fn do_remap_range(r: SeedRange, mapper: Mapper, acc: List(SeedRange)) { ms, [ SRange(r.start, m.start - 1), - SRange(m.start + m.offset, m.end + m.offset), + transform_range(SRange(m.start, m.end), m), ..acc ], ) } } -fn remap_all_seed_ranges(srs: List(SeedRange), mappers: List(Mapper)) { - case mappers { - [] -> srs - [mapper, ..rest] -> - list.flat_map(srs, remap_range(_, mapper)) - |> remap_all_seed_ranges(rest) - } -} - -fn compare_ranges(range1: SeedRange, range2: SeedRange) { - int.compare(range1.start, range2.start) -} - -pub fn part2(input: String) { - let Almanac(seeds, mappers) = parse_input(input) - - let [SRange(answer, _), ..] = - seeds - |> list.sized_chunk(into: 2) - |> list.map(fn(chunk) { - let [start, length] = chunk - [SRange(start, start + length - 1)] - |> remap_all_seed_ranges(mappers) - }) - |> list.flatten() - |> list.unique() - |> list.sort(compare_ranges) - |> io.debug() - - string.inspect(answer) -} - pub fn main() { let assert Ok(part) = adglent.get_part() let assert Ok(input) = adglent.get_input("5") |