aboutsummaryrefslogtreecommitdiff
path: root/aoc2023-gleam/src/day8/solve.gleam
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2023-gleam/src/day8/solve.gleam')
-rw-r--r--aoc2023-gleam/src/day8/solve.gleam91
1 files changed, 91 insertions, 0 deletions
diff --git a/aoc2023-gleam/src/day8/solve.gleam b/aoc2023-gleam/src/day8/solve.gleam
new file mode 100644
index 0000000..6b36e2d
--- /dev/null
+++ b/aoc2023-gleam/src/day8/solve.gleam
@@ -0,0 +1,91 @@
+import adglent.{First, Second}
+import gleam/bool
+import gleam/dict.{type Dict}
+import gleam/io
+import gleam/iterator.{type Iterator, Next}
+import gleam/list
+import gleam/option.{Some}
+import gleam/string
+import gleam/regex.{type Match, Match}
+import gleam_community/maths/arithmetics
+
+type Paths {
+ Paths(to_left: String, to_right: String)
+}
+
+type Maze =
+ Dict(String, Paths)
+
+fn parse(input: String) -> #(Iterator(String), Dict(String, Paths)) {
+ let assert [directions_str, maze_str] = string.split(input, "\n\n")
+
+ let directions =
+ directions_str
+ |> string.to_graphemes()
+ |> iterator.from_list
+ |> iterator.cycle
+
+ let assert Ok(re) = regex.from_string("(...) = \\((...), (...)\\)")
+ let maze =
+ maze_str
+ |> string.split("\n")
+ |> list.map(fn(str) {
+ let assert [Match(submatches: [Some(name), Some(left), Some(right)], ..)] =
+ regex.scan(re, str)
+ #(name, Paths(left, right))
+ })
+ |> dict.from_list
+
+ #(directions, maze)
+}
+
+fn to_next_step(
+ current: String,
+ stop_at: String,
+ count: Int,
+ directions: Iterator(String),
+ maze: Maze,
+) -> Int {
+ use <- bool.guard(string.ends_with(current, stop_at), count)
+ let assert Next(next_direction, rest_directions) = iterator.step(directions)
+ let assert Ok(paths) = dict.get(maze, current)
+ case next_direction {
+ "L" -> paths.to_left
+ "R" -> paths.to_right
+ _ -> panic as "bad direction"
+ }
+ |> to_next_step(stop_at, count + 1, rest_directions, maze)
+}
+
+pub fn part1(input: String) -> Int {
+ let #(directions, maze) = parse(input)
+
+ to_next_step("AAA", "ZZZ", 0, directions, maze)
+}
+
+pub fn part2(input: String) -> Int {
+ let #(directions, maze) = parse(input)
+
+ use acc, name <- list.fold(dict.keys(maze), 1)
+ case string.ends_with(name, "A") {
+ False -> acc
+ True ->
+ to_next_step(name, "Z", 0, directions, maze)
+ |> arithmetics.lcm(acc)
+ }
+}
+
+pub fn main() {
+ let assert Ok(part) = adglent.get_part()
+ let assert Ok(input) = adglent.get_input("8")
+ case part {
+ First ->
+ part1(input)
+ |> adglent.inspect
+ |> io.println
+ Second ->
+ part2(input)
+ |> adglent.inspect
+ |> io.println
+ }
+}