diff options
Diffstat (limited to 'aoc2023/src/day4/solve.gleam')
-rw-r--r-- | aoc2023/src/day4/solve.gleam | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/aoc2023/src/day4/solve.gleam b/aoc2023/src/day4/solve.gleam new file mode 100644 index 0000000..426922c --- /dev/null +++ b/aoc2023/src/day4/solve.gleam @@ -0,0 +1,129 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/int +import gleam/list +import gleam/dict +import gleam/result +import gleam/option.{None, Some} + +type Card { + Card(number: Int, winning: List(Int), has: List(Int)) +} + +fn numbers_to_list(str: String) -> List(Int) { + str + |> string.split(" ") + |> list.map(int.parse) + |> result.values() +} + +fn parse_card(card: String) { + let assert Ok(#("Card" <> n_str, rest)) = string.split_once(card, ": ") + let assert Ok(#(winning_str, has_str)) = string.split_once(rest, " | ") + let assert Ok(n) = + n_str + |> string.trim + |> int.parse + + let winning = numbers_to_list(winning_str) + let has = numbers_to_list(has_str) + + Card(number: n, winning: winning, has: has) +} + +fn double_up(n) { + case n { + 0 -> 0 + 1 -> 1 + n -> 2 * double_up(n - 1) + } +} + +fn count_wins(card: Card) { + list.fold( + card.has, + 0, + fn(acc, c) { + case list.contains(card.winning, c) { + True -> acc + 1 + False -> acc + } + }, + ) +} + +pub fn part1(input: String) { + input + |> string.split("\n") + |> list.fold( + 0, + fn(acc, c) { + c + |> parse_card + |> count_wins + |> double_up + |> int.add(acc) + }, + ) + |> string.inspect +} + +fn win_more_cards(cards: List(Card), card_count: dict.Dict(Int, Int)) { + case cards { + [] -> + card_count + |> dict.values + |> int.sum + [card, ..rest] -> { + let wins = count_wins(card) + case wins { + 0 -> win_more_cards(rest, card_count) + n -> { + let assert Ok(bonus) = dict.get(card_count, card.number) + let won_cards = list.range(card.number + 1, card.number + n) + { + use acc, n <- list.fold(won_cards, card_count) + use c <- dict.update(acc, n) + case c { + Some(i) -> i + bonus + None -> panic as "won a card that doesn't exist in the card pile" + } + } + |> win_more_cards(rest, _) + } + } + } + } +} + +pub fn part2(input: String) { + let raw_cards = + input + |> string.split("\n") + + let card_count = + list.range(1, list.length(raw_cards)) + |> list.map(fn(n) { #(n, 1) }) + |> dict.from_list() + + raw_cards + |> list.map(parse_card) + |> win_more_cards(card_count) + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("4") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} |