my solutions to advent of code
aoc
advent-of-code
1import gleam/dict.{type Dict}
2import gleam/int
3import gleam/io
4import gleam/list
5import gleam/order.{Eq, Gt, Lt}
6import gleam/result
7import gleam/set.{type Set}
8import gleam/string
9import simplifile as file
10
11pub type Reindeer {
12 Reindeer(name: String, speed: Int, run: Int, rest: Int)
13}
14
15pub type Action {
16 Run
17 Rest
18}
19
20pub type ReindeerState {
21 ReindeerState(points: Int, km: Int, action: Action, time_until_change: Int)
22}
23
24pub type LeadKm {
25 LeadKm(km: Int, leads: Set(String))
26}
27
28pub type RunState =
29 Dict(String, ReindeerState)
30
31pub fn to_int(number_string) {
32 let assert Ok(number) = number_string |> int.base_parse(10)
33 as { number_string <> " is not a number" }
34 number
35}
36
37pub fn do_calculate_run_state(state: RunState, reindeers: List(Reindeer), sec) {
38 case sec > 0 {
39 False -> state
40 True -> {
41 let #(LeadKm(_, leads), state) =
42 reindeers
43 |> list.fold(#(LeadKm(0, set.new()), state), fn(s, reindeer) {
44 let #(lead, state) = s
45 let Reindeer(name, speed, run, rest) = reindeer
46 let ReindeerState(points, km, action, time_until_change) =
47 state
48 |> dict.get(reindeer.name)
49 |> result.unwrap(ReindeerState(0, 0, Run, run))
50
51 let km = case action {
52 Run -> km + speed
53 Rest -> km
54 }
55 let state =
56 case time_until_change, action {
57 1, Run -> ReindeerState(points, km, Rest, rest)
58 1, Rest -> ReindeerState(points, km, Run, run)
59 _, _ -> ReindeerState(points, km, action, time_until_change - 1)
60 }
61 |> dict.insert(state, name, _)
62
63 let lead = case int.compare(km, lead.km) {
64 Eq -> LeadKm(km, lead.leads |> set.insert(name))
65 Gt -> LeadKm(km, set.new() |> set.insert(name))
66 Lt -> lead
67 }
68
69 #(lead, state)
70 })
71
72 leads
73 |> set.fold(state, fn(state, reindeer_name) {
74 let assert Ok(ReindeerState(points, km, action, time_until_change)) =
75 state
76 |> dict.get(reindeer_name)
77 state
78 |> dict.insert(
79 reindeer_name,
80 ReindeerState(points + 1, km, action, time_until_change),
81 )
82 })
83 |> do_calculate_run_state(reindeers, sec - 1)
84 }
85 }
86}
87
88pub fn calculate_run_state(reindeers: List(Reindeer), sec: Int) {
89 do_calculate_run_state(dict.new(), reindeers, sec)
90}
91
92pub fn get_winner_km(state: RunState) {
93 state
94 |> dict.fold(0, fn(winner, _, state) {
95 case state.km > winner {
96 True -> state.km
97 False -> winner
98 }
99 })
100}
101
102pub fn get_winner_points(state: RunState) {
103 state
104 |> dict.fold(0, fn(winner, _, state) {
105 case state.points > winner {
106 True -> state.points
107 False -> winner
108 }
109 })
110}
111
112pub fn main() {
113 let assert Ok(reindeer) = file.read(from: "../input.txt")
114 as "Input file not found"
115 let reindeer =
116 reindeer
117 |> string.trim
118 |> string.split("\n")
119 |> list.map(fn(v) {
120 case v |> string.split(" ") {
121 // (name) can fly (speed) km/s for (run) seconds, but then must rest for (rest) seconds.
122 [name, _, _, speed, _, _, run, _, _, _, _, _, _, rest, _] ->
123 Reindeer(name, speed |> to_int, run |> to_int, rest |> to_int)
124 _ -> panic as { v <> " is not a valid line" }
125 }
126 })
127
128 "Part 1" |> io.println
129 reindeer
130 |> calculate_run_state(2503)
131 |> get_winner_km
132 |> int.to_string
133 |> io.println
134
135 "Part 2" |> io.println
136 reindeer
137 |> calculate_run_state(2503)
138 |> get_winner_points
139 |> int.to_string
140 |> io.println
141}