1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
}
}
|