169 lines
3.9 KiB
Gleam
169 lines
3.9 KiB
Gleam
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
|
|
}
|