import gleam/int import gleam/io import gleam/list import gleam/order import gleam/pair import gleam/result import gleam/string import simplifile pub type Bank { Bank(List(Digit)) } pub type Digit { Digit(Int) } pub fn bank_parse(s: String) -> Bank { string.trim(s) |> int.parse |> result.map(digits(_, 10)) |> result.flatten |> result.unwrap([]) |> list.map(Digit) |> Bank } pub fn file_to_banks(f: String) -> List(Bank) { simplifile.read(f) |> result.unwrap("") |> string.trim |> string.split("\n") |> list.map(bank_parse) } fn digits(x: Int, base: Int) -> Result(List(Int), Nil) { case base < 2 { True -> Error(Nil) False -> Ok(digits_loop(x, base, [])) } } fn digits_loop(x: Int, base: Int, acc: List(Int)) -> List(Int) { case int.absolute_value(x) < base { True -> [x, ..acc] False -> digits_loop(x / base, base, [x % base, ..acc]) } } fn digit_outof(d: Digit) -> Int { let Digit(d) = d d } pub fn undigits(numbers: List(Int), base: Int) -> Result(Int, Nil) { case base < 2 { True -> Error(Nil) False -> undigits_loop(numbers, base, 0) } } fn undigits_loop(numbers: List(Int), base: Int, acc: Int) -> Result(Int, Nil) { case numbers { [] -> Ok(acc) [digit, ..] if digit >= base -> Error(Nil) [digit, ..rest] -> undigits_loop(rest, base, acc * base + digit) } } pub fn int_list_to_bank(l: List(Int)) -> Bank { Bank(list.map(l, Digit)) } pub fn prepend_bank_with_max_joltage( new_digit: Digit, curr: #(Bank, List(Digit)), length: Int, ) -> #(Bank, List(Digit)) { let #(old_bank, old_max) = curr case new_digit, old_bank, old_max, length { _, Bank([]), [_, ..], _ -> panic as "old_bank should be longer than old_max" Digit(n), Bank(b), max, length -> case list.length(max), length { m, l if m > l -> panic as "length of max should never exceed target lenght" m, l if m < l -> #( Bank(list.append([Digit(n)], b)), list.append([Digit(n)], max), ) m, l if m == l -> { case n, max { n, [] -> #(Bank(list.append([Digit(n)], b)), [Digit(n)]) n, [Digit(h), ..t] if n >= h -> { let #(_, inner) = prepend_bank_with_max_joltage( Digit(h), #( Bank(list.drop(b, 1)), t, // |> list.map(digit_outof) // |> drop_first_min // |> list.map(Digit), ), length, ) #( Bank(list.append([Digit(n)], b)), list.prepend( inner |> list.map(digit_outof) |> drop_first_min |> list.map(Digit), Digit(n), ), ) } n, m -> { #(Bank(list.append([Digit(n)], b)), m) } } } _, _ -> panic as "Kris, where the fuck are we?" } |> pair.map_second(fn(l) { list.map(l, digit_outof) // |> echo |> list.map(Digit) }) } } pub fn drop_first_min(l: List(Int)) -> List(Int) { let min = list.max(l, int.compare |> order.reverse) |> result.unwrap(-1) let #(left, right) = l |> list.split_while(fn(n) { n > min }) |> pair.map_second(fn(s) { case s { [] -> [] [_] -> [] [_, ..t] -> t } }) list.append(left, right) } pub fn max_joltage_from_bank(b: Bank) { let Bank(b) = b let #(_, max) = b |> list.fold_right(#(Bank([]), []), fn(p, d) { prepend_bank_with_max_joltage(d, p, 12) }) max |> list.map(digit_outof) |> undigits(10) |> result.unwrap(0) } pub fn file_to_max_joltage(f: String) { file_to_banks(f) |> list.map(max_joltage_from_bank) |> echo |> list.fold(0, int.add) } pub fn main() -> Nil { file_to_max_joltage("02.txt") |> int.to_string |> io.println }