|
| 1 | +module main |
| 2 | + |
| 3 | +import arrays |
| 4 | +import math |
| 5 | +import os |
| 6 | + |
| 7 | +struct Player { |
| 8 | + raw_hand string |
| 9 | + hand_rank int |
| 10 | + hand_rank_with_jokers int |
| 11 | + bet int |
| 12 | +} |
| 13 | + |
| 14 | +fn main() { |
| 15 | + card_labels := [`A`, `K`, `Q`, `J`, `T`, `9`, `8`, `7`, `6`, `5`, `4`, `3`, `2`] |
| 16 | + joker_labels := [`A`, `K`, `Q`, `T`, `9`, `8`, `7`, `6`, `5`, `4`, `3`, `2`, `J`] |
| 17 | + |
| 18 | + hands := os.read_lines('camel_cards.input')! |
| 19 | + .map(it.split(' ')) |
| 20 | + .map(fn [joker_labels] (line []string) Player { |
| 21 | + runes := line[0].runes() |
| 22 | + return Player{ |
| 23 | + raw_hand: line[0] |
| 24 | + hand_rank: rank_hand(runes) |
| 25 | + hand_rank_with_jokers: rank_with_jokers(runes, joker_labels#[..-1]) |
| 26 | + bet: line[1].int() |
| 27 | + } |
| 28 | + }) |
| 29 | + |
| 30 | + part_one := sum_scores(hands, card_labels, |p| p.hand_rank) |
| 31 | + part_two := sum_scores(hands, joker_labels, |p| p.hand_rank_with_jokers) |
| 32 | + |
| 33 | + println('Part 1: ${part_one}') |
| 34 | + println('Part 2: ${part_two}') |
| 35 | +} |
| 36 | + |
| 37 | +fn sum_scores(hands []Player, labels []rune, rank_func fn (player &Player) int) int { |
| 38 | + sorted := hands.sorted_with_compare(fn [labels, rank_func] (a &Player, b &Player) int { |
| 39 | + return sort_hands(a, b, labels, rank_func) |
| 40 | + }) |
| 41 | + |
| 42 | + mut sum := 0 |
| 43 | + for i in 0 .. sorted.len { |
| 44 | + sum += sorted[i].bet * (i + 1) |
| 45 | + } |
| 46 | + |
| 47 | + return sum |
| 48 | +} |
| 49 | + |
| 50 | +fn sort_hands(a Player, b Player, labels []rune, rank_func fn (player &Player) int) int { |
| 51 | + a_rank := rank_func(a) |
| 52 | + b_rank := rank_func(b) |
| 53 | + |
| 54 | + if a_rank == b_rank { |
| 55 | + for i in 0 .. a.raw_hand.len { |
| 56 | + if a.raw_hand[i] != b.raw_hand[i] { |
| 57 | + return labels.index(b.raw_hand[i]) - labels.index(a.raw_hand[i]) |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + return b_rank - a_rank |
| 63 | +} |
| 64 | + |
| 65 | +fn rank_hand(hand []rune) int { |
| 66 | + values := arrays.map_of_counts(hand).values() |
| 67 | + if 5 in values { |
| 68 | + return 0 |
| 69 | + } else if 4 in values { |
| 70 | + return 1 |
| 71 | + } else if 3 in values { |
| 72 | + if 2 in values { |
| 73 | + return 2 |
| 74 | + } |
| 75 | + |
| 76 | + return 3 |
| 77 | + } else { |
| 78 | + pairs := values.filter(it == 2).len |
| 79 | + match pairs { |
| 80 | + 2 { return 4 } |
| 81 | + 1 { return 5 } |
| 82 | + else {} |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + return 6 |
| 87 | +} |
| 88 | + |
| 89 | +fn rank_with_jokers(hand []rune, labels []rune) int { |
| 90 | + if `J` !in hand { |
| 91 | + return rank_hand(hand) |
| 92 | + } |
| 93 | + |
| 94 | + mut top_rank := 6 |
| 95 | + mut queue := [hand] |
| 96 | + for queue.len > 0 { |
| 97 | + curr := queue.pop() |
| 98 | + idx := curr.index(`J`) |
| 99 | + prefix := curr[..idx] |
| 100 | + suffix := curr[idx + 1..curr.len] |
| 101 | + |
| 102 | + for label in labels { |
| 103 | + mut new_hand := []rune{} |
| 104 | + new_hand << prefix |
| 105 | + new_hand << [label] |
| 106 | + new_hand << suffix |
| 107 | + |
| 108 | + if `J` in new_hand { |
| 109 | + queue << new_hand |
| 110 | + } else { |
| 111 | + top_rank = math.min(top_rank, rank_hand(new_hand)) |
| 112 | + if top_rank == 0 { |
| 113 | + return top_rank |
| 114 | + } |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + return top_rank |
| 120 | +} |
0 commit comments