aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/src/day11/solve.gleam
blob: 35464a15dfe4d3e186285b0c93c5d5b2c278e054 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import adglent.{First, Second}
import gleam/io
import gleam/int
import gleam/string
import gleam/list

type Posn {
  Posn(x: Int, y: Int)
}

fn find_empty(grid: List(List(String))) {
  use acc, row, r <- list.index_fold(grid, [])
  case list.unique(row) {
    ["."] -> [r, ..acc]
    _ -> acc
  }
}

fn count_prior_empty_ranks(rank: Int, empty_ranks: List(Int)) -> Int {
  empty_ranks
  |> list.drop_while(fn(r_empty) { r_empty > rank })
  |> list.length
}

fn parse_with_expansion(input: String, expansion: Int) -> List(Posn) {
  let add = expansion - 1
  let grid =
    input
    |> string.split("\n")
    |> list.map(string.to_graphemes)

  let empty_row_list = find_empty(grid)
  let empty_col_list = find_empty(list.transpose(grid))

  {
    use r, row <- list.index_map(grid)
    use acc, cell, c <- list.index_fold(over: row, from: [])

    let p = Posn(r, c)
    let empty_r = count_prior_empty_ranks(r, empty_row_list)
    let empty_c = count_prior_empty_ranks(c, empty_col_list)
    case cell {
      "#" -> [Posn(p.x + empty_r * add, p.y + empty_c * add), ..acc]
      _empty -> acc
    }
  }
  |> list.flatten()
}

fn all_distances(stars: List(Posn)) -> Int {
  use acc, pair <- list.fold(list.combination_pairs(stars), 0)
  let #(s1, s2) = pair
  acc + int.absolute_value(s1.x - s2.x) + int.absolute_value(s1.y - s2.y)
}

fn find_distances(input: String, expand_by: Int) -> String {
  input
  |> parse_with_expansion(expand_by)
  |> all_distances
  |> string.inspect
}

pub fn part1(input: String) {
  find_distances(input, 2)
}

pub fn part2(input: String) {
  find_distances(input, 1_000_000)
}

pub fn main() {
  let assert Ok(part) = adglent.get_part()
  let assert Ok(input) = adglent.get_input("11")
  case part {
    First ->
      part1(input)
      |> adglent.inspect
      |> io.println
    Second ->
      part2(input)
      |> adglent.inspect
      |> io.println
  }
}