import gleam/bool import gleam/dict.{type Dict} import gleam/int import gleam/io import gleam/iterator import gleam/list.{filter, filter_map, fold, map, reverse} import gleam/option.{type Option, None, Some} import gleam/result import gleam/string import stdin.{stdin} pub fn main() { stdin() |> read_input |> calculate_trails |> io.debug } fn calculate_trails(topomap: Dict(#(Int, Int), Int)) -> Int { topomap |> find_trailheads |> map(fn(x) { count_trails_loop(topomap, x, 0) }) |> map(list.length) |> fold(0, int.add) } fn count_trails_loop( topomap: Dict(#(Int, Int), Int), pos: #(Int, Int), offset: Int, ) -> List(#(Int, Int)) { let #(x, y) = pos case dict.get(topomap, pos) { Ok(9) if offset == 9 -> { [pos] } Ok(n) if n == offset -> [ count_trails_loop(topomap, #(x, y - 1), offset + 1), count_trails_loop(topomap, #(x, y + 1), offset + 1), count_trails_loop(topomap, #(x - 1, y), offset + 1), count_trails_loop(topomap, #(x + 1, y), offset + 1), ] |> filter(fn(x) { x |> list.is_empty |> bool.negate }) |> list.flatten |> list.unique _ -> [] } } fn find_trailheads(topomap: Dict(#(Int, Int), Int)) -> List(#(Int, Int)) { topomap |> dict.filter(fn(_, v) { v == 0 }) |> dict.keys } fn read_input(it: iterator.Iterator(String)) -> Dict(#(Int, Int), Int) { it |> iterator.fold([], fn(acc, line) { case string.trim(line) { "" -> acc str -> { [str |> string.to_graphemes |> filter_map(int.parse), ..acc] } } }) |> reverse |> list.index_fold(dict.new(), fn(acc, line, y) { line |> list.index_fold(acc, fn(dic, num, x) { dict.insert(dic, #(x, y), num) }) }) }