aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/src/day23/solve.gleam
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2023/src/day23/solve.gleam')
-rw-r--r--aoc2023/src/day23/solve.gleam194
1 files changed, 0 insertions, 194 deletions
diff --git a/aoc2023/src/day23/solve.gleam b/aoc2023/src/day23/solve.gleam
deleted file mode 100644
index e1fe638..0000000
--- a/aoc2023/src/day23/solve.gleam
+++ /dev/null
@@ -1,194 +0,0 @@
-import adglent.{First, Second}
-import gleam/int
-import gleam/io
-import gleam/dict.{type Dict}
-import gleam/list
-import gleam/option.{type Option, None, Some}
-import gleam/string
-import gleam/set.{type Set}
-import gleam/bool
-import utilities/array2d.{type Array2D, type Posn, Posn}
-
-type Path {
- Unknown
- Straight
- Junction
-}
-
-type Route {
- Route(to: Posn, distance: Int)
-}
-
-fn append_to_key(v: Option(List(a)), new: a) -> List(a) {
- case v {
- None -> [new]
- Some(xs) -> [new, ..xs]
- }
-}
-
-fn first_parse_path(c: String) -> Result(Path, Nil) {
- case c {
- "#" -> Error(Nil)
- _ -> Ok(Unknown)
- }
-}
-
-fn junction_neighbors(p: Posn) -> List(Posn) {
- [Posn(..p, r: p.r + 1), Posn(..p, c: p.c + 1)]
-}
-
-fn mark_junctions(trails: Array2D(Path)) -> Array2D(Path) {
- use trail, _ <- dict.map_values(trails)
-
- let valid_neighbors =
- trail
- |> array2d.ortho_neighbors
- |> list.filter(dict.has_key(trails, _))
-
- case list.length(valid_neighbors) {
- 2 -> Straight
- _ -> Junction
- }
-}
-
-fn start_walking_to_next_junction(
- start: Posn,
- next: Posn,
- trails: Array2D(Path),
-) {
- let seen =
- set.new()
- |> set.insert(start)
- |> set.insert(next)
- walk_to_next_junction(start, next, 1, seen, trails)
-}
-
-fn walk_to_next_junction(
- start: Posn,
- current: Posn,
- length: Int,
- seen: Set(Posn),
- trails: Array2D(Path),
-) -> #(Posn, Route) {
- let assert [next] =
- current
- |> array2d.ortho_neighbors
- |> list.filter(fn(n) { dict.has_key(trails, n) && !set.contains(seen, n) })
-
- case dict.get(trails, next) {
- Ok(Junction) -> #(start, Route(to: next, distance: length + 1))
- _ -> {
- let seen = set.insert(seen, current)
- walk_to_next_junction(start, next, { length + 1 }, seen, trails)
- }
- }
-}
-
-fn find_routes(junctions, trails) {
- use junction <- list.flat_map(junctions)
- use neighbor <- list.filter_map(junction_neighbors(junction))
- case dict.has_key(trails, neighbor) {
- True -> Ok(start_walking_to_next_junction(junction, neighbor, trails))
- False -> Error(Nil)
- }
-}
-
-fn generate_routes(
- junctions: List(Posn),
- trails: Array2D(Path),
-) -> Dict(Posn, List(Route)) {
- use acc, #(from, route) <- list.fold(
- find_routes(junctions, trails),
- dict.new(),
- )
- dict.update(acc, from, append_to_key(_, route))
-}
-
-fn generate_2way_routes(
- junctions: List(Posn),
- trails: Array2D(Path),
-) -> Dict(Posn, List(Route)) {
- use acc, #(from, route) <- list.fold(
- find_routes(junctions, trails),
- dict.new(),
- )
- acc
- |> dict.update(from, append_to_key(_, route))
- |> dict.update(route.to, append_to_key(_, Route(from, route.distance)))
-}
-
-fn dfs(routes, from, to) {
- let seen = set.insert(set.new(), from)
- do_dfs(routes, from, to, 0, seen)
-}
-
-fn do_dfs(
- routes: Dict(Posn, List(Route)),
- from: Posn,
- to: Posn,
- acc: Int,
- seen: Set(Posn),
-) -> Int {
- use <- bool.guard(to == from, acc)
-
- let assert Ok(all_routes) = dict.get(routes, from)
- let neighbors = list.filter(all_routes, fn(r) { !set.contains(seen, r.to) })
-
- case neighbors {
- [] -> 0
- neighbors ->
- list.fold(neighbors, acc, fn(inner_acc, n) {
- let score =
- do_dfs(routes, n.to, to, acc + n.distance, set.insert(seen, n.to))
- int.max(score, inner_acc)
- })
- }
-}
-
-fn solve_using(
- input: String,
- using: fn(List(Posn), Dict(Posn, Path)) -> Dict(Posn, List(Route)),
-) -> Int {
- let min_row = 0
- let max_row = list.length(string.split(input, "\n")) - 1
-
- let trails =
- input
- |> array2d.parse_grid_using(first_parse_path)
- |> mark_junctions
-
- let junctions =
- trails
- |> dict.filter(fn(_, v) { v == Junction })
- |> dict.keys
-
- let assert Ok(start) = list.find(junctions, fn(j) { j.r == min_row })
- let assert Ok(end) = list.find(junctions, fn(j) { j.r == max_row })
-
- let routes = using(junctions, trails)
-
- dfs(routes, start, end)
-}
-
-pub fn part1(input: String) {
- solve_using(input, generate_routes)
-}
-
-pub fn part2(input: String) {
- solve_using(input, generate_2way_routes)
-}
-
-pub fn main() {
- let assert Ok(part) = adglent.get_part()
- let assert Ok(input) = adglent.get_input("23")
- case part {
- First ->
- part1(input)
- |> adglent.inspect
- |> io.println
- Second ->
- part2(input)
- |> adglent.inspect
- |> io.println
- }
-}