From 24c2bbc3d0f652b4e3fcbcf0a60691da75db2abd Mon Sep 17 00:00:00 2001
From: Hamcha <hamcha@crunchy.rocks>
Date: Tue, 10 Dec 2024 09:58:31 +0100
Subject: [PATCH] day 10

---
 src/day10_p1.gleam | 70 ++++++++++++++++++++++++++++++++++++++++++++++
 src/day10_p2.gleam | 69 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 139 insertions(+)
 create mode 100644 src/day10_p1.gleam
 create mode 100644 src/day10_p2.gleam

diff --git a/src/day10_p1.gleam b/src/day10_p1.gleam
new file mode 100644
index 0000000..f5131be
--- /dev/null
+++ b/src/day10_p1.gleam
@@ -0,0 +1,70 @@
+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) })
+  })
+}
diff --git a/src/day10_p2.gleam b/src/day10_p2.gleam
new file mode 100644
index 0000000..a23e89b
--- /dev/null
+++ b/src/day10_p2.gleam
@@ -0,0 +1,69 @@
+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
+    _ -> []
+  }
+}
+
+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) })
+  })
+}