aboutsummaryrefslogtreecommitdiff
path: root/aoc2023-gleam/src/day7/solve.gleam
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2023-gleam/src/day7/solve.gleam')
-rw-r--r--aoc2023-gleam/src/day7/solve.gleam140
1 files changed, 140 insertions, 0 deletions
diff --git a/aoc2023-gleam/src/day7/solve.gleam b/aoc2023-gleam/src/day7/solve.gleam
new file mode 100644
index 0000000..4454883
--- /dev/null
+++ b/aoc2023-gleam/src/day7/solve.gleam
@@ -0,0 +1,140 @@
+import adglent.{First, Second}
+import gleam/bool
+import gleam/function
+import gleam/int
+import gleam/io
+import gleam/list
+import gleam/order.{type Order, Eq, Lt}
+import gleam/string
+
+// Types -------------------------------------------------------------------------------------------
+
+type Hand {
+ Hand(cards: List(Int), wager: Int)
+}
+
+// Common functions --------------------------------------------------------------------------------
+
+fn parse_hand(str: String) -> Hand {
+ let assert [cards, wager] = string.split(str, " ")
+ let cards =
+ string.to_graphemes(cards)
+ |> list.map(card_rank)
+ let assert Ok(wager) = int.parse(wager)
+
+ Hand(cards, wager)
+}
+
+fn classify_hand(hand: Hand) -> Int {
+ case list.length(list.unique(hand.cards)), card_counts(hand) {
+ 1, _ -> 8
+ 2, [1, 4] -> 7
+ 2, [2, 3] -> 6
+ 3, [1, 1, 3] -> 5
+ 3, [1, 2, 2] -> 4
+ 4, _ -> 3
+ 5, _ -> 2
+ _, _ -> 1
+ }
+}
+
+fn card_counts(hand: Hand) {
+ hand.cards
+ |> list.sort(int.compare)
+ |> list.chunk(function.identity)
+ |> list.map(list.length)
+ |> list.sort(int.compare)
+}
+
+fn card_rank(card: String) -> Int {
+ case int.parse(card), card {
+ Ok(n), _ -> n
+ _, "A" -> 14
+ _, "K" -> 13
+ _, "Q" -> 12
+ _, "J" -> 11
+ _, "T" -> 10
+ _, _ -> 1
+ }
+}
+
+fn compare_hands(hand1: Hand, hand2: Hand, using: fn(Hand) -> Int) -> Order {
+ case int.compare(using(hand1), using(hand2)) {
+ Eq -> compare_top_card(hand1.cards, hand2.cards)
+ other -> other
+ }
+}
+
+fn compare_top_card(cards1: List(Int), cards2: List(Int)) -> Order {
+ use <- bool.guard(cards1 == [] || cards2 == [], Eq)
+ let assert [c1, ..rest1] = cards1
+ let assert [c2, ..rest2] = cards2
+ case int.compare(c1, c2) {
+ Eq -> compare_top_card(rest1, rest2)
+ other -> other
+ }
+}
+
+fn part(input: String, comparator: fn(Hand, Hand) -> Order) {
+ input
+ |> string.split("\n")
+ |> list.map(parse_hand)
+ |> list.sort(comparator)
+ |> list.index_map(fn(i, h) { { i + 1 } * h.wager })
+ |> int.sum
+ |> string.inspect
+}
+
+// Part 1 ------------------------------------------------------------------------------------------
+
+pub fn part1(input: String) {
+ part(input, compare_without_wilds)
+}
+
+fn compare_without_wilds(hand1: Hand, hand2: Hand) {
+ compare_hands(hand1, hand2, classify_hand)
+}
+
+// Part 2 ------------------------------------------------------------------------------------------
+
+pub fn part2(input: String) {
+ part(string.replace(input, "J", "*"), compare_hands_considering_jokers)
+}
+
+fn find_best_joker_substitution(hand: Hand) {
+ use acc, card <- list.fold(list.range(2, 14), Hand([], 0))
+ let subbed_cards = {
+ use c <- list.map(hand.cards)
+ case c {
+ 1 -> card
+ other -> other
+ }
+ }
+ let subbed_hand = Hand(..hand, cards: subbed_cards)
+ case compare_hands(acc, subbed_hand, classify_hand) {
+ Lt -> subbed_hand
+ _ -> acc
+ }
+}
+
+fn compare_hands_considering_jokers(hand1: Hand, hand2: Hand) -> Order {
+ use hand <- compare_hands(hand1, hand2)
+ hand
+ |> find_best_joker_substitution
+ |> classify_hand
+}
+
+pub fn main() {
+ let assert Ok(part) = adglent.get_part()
+ let assert Ok(input) = adglent.get_input("7")
+ case part {
+ First ->
+ part1(input)
+ |> adglent.inspect
+ |> io.println
+ Second ->
+ part2(input)
+ |> adglent.inspect
+ |> io.println
+ }
+}