aboutsummaryrefslogtreecommitdiff
path: root/aoc-2020-gleam/src/days/day13.gleam
blob: a7307dd986a60b7bebd639e03bddac31afcec38d (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import gleam/io
import gleam/int
import gleam/list
import gleam/pair
import gleam/string as str
import gleam/iterator as iter
import ext/intx
import ext/resultx as resx
import ext/iteratorx as iterx
import util/input_util

type Bus {
  Any
  Line(Int)
}

type Problem {
  Problem(timestamp: Int, buses: List(Bus))
}

fn parse_problem(input: String) -> Problem {
  let assert Ok(#(timestamp, buses)) =
    input
    |> str.trim
    |> str.split_once(on: "\n")
  let assert Ok(timestamp) = int.parse(timestamp)
  let buses =
    buses
    |> str.split(on: ",")
    |> list.map(with: fn(b) {
      case b == "x" {
        True -> Any
        False ->
          b
          |> int.parse
          |> resx.assert_unwrap
          |> Line
      }
    })
  Problem(timestamp, buses)
}

fn part1(input: String) -> Int {
  let problem = parse_problem(input)

  problem.buses
  |> list.filter_map(with: fn(b) {
    case b {
      Line(line) -> Ok(line)
      Any -> Error(Nil)
    }
  })
  |> list.map(with: fn(b) {
    #(b, b * intx.ceil_divide(problem.timestamp, by: b) - problem.timestamp)
  })
  |> list.reduce(with: fn(acc, cur) {
    case cur.1 < acc.1 {
      True -> cur
      False -> acc
    }
  })
  |> resx.assert_unwrap
  |> fn(res: #(Int, Int)) { res.0 * res.1 }
}

fn part2(input: String) -> Int {
  let problem = parse_problem(input)

  let buses =
    problem.buses
    |> iter.from_list
    |> iter.index
    |> iter.flat_map(fn(entry) {
      let #(b, i) = entry
      case b {
        Line(line) -> iter.single(#(line, i))
        Any -> iter.empty()
      }
    })
    |> iter.to_list

  let assert [#(timestamp, _), ..] = buses

  buses
  |> list.fold(from: #(timestamp, timestamp), with: fn(prev, entry) {
    let #(timestamp, period) = prev
    let #(id, i) = entry

    let assert Ok(timestamp) =
      timestamp
      |> iterx.unfold_infinitely(with: int.add(_, period))
      |> iter.find(one_that: fn(t) { { t + i } % id == 0 })
    let period = intx.lcm(period, id)

    #(timestamp, period)
  })
  |> pair.first
}

pub fn main() -> Nil {
  let testing = input_util.read_text("test13")
  let assert 295 = part1(testing)
  let assert 1_068_781 = part2(testing)

  let input = input_util.read_text("day13")
  io.debug(part1(input))
  io.debug(part2(input))

  Nil
}