-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathq4.cpp
165 lines (139 loc) · 5.39 KB
/
q4.cpp
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <boost/algorithm/string/classification.hpp> // for boost::is_any_of
#include <boost/algorithm/string/split.hpp> // for boost::split
#include <cassert>
#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <map>
#include <numeric>
using IntVec = std::vector<int>;
using StrVec = std::vector<std::string>;
using Card = std::pair<IntVec, IntVec>;
using CardWithStrVals = std::pair<StrVec, StrVec>;
// type conversions
template<typename T>
std::vector<T> vectorize(std::istream& is) {
std::vector<T> vec;
T str;
while(std::getline(is, str)){
vec.push_back(str);
}
return vec;
}
std::vector<StrVec> tokenize(const StrVec& lines_of_data) {
std::vector<StrVec> tokenized_lines_of_data;
for (const auto& line : lines_of_data) {
StrVec tokens;
boost::split(tokens, line, boost::is_any_of(" "), boost::token_compress_on);
tokenized_lines_of_data.push_back(tokens);
}
return tokenized_lines_of_data;
}
IntVec str_to_int_vec(const StrVec& vec) {
IntVec i_vec;
for (auto& num : vec) {
i_vec.push_back(stoi(num));
}
return i_vec;
}
bool is_num_token(std::string_view token) {
/// helper function for split_str_vec()
return std::ranges::all_of(token, isdigit);
}
CardWithStrVals split_str_vec(const StrVec& vec) {
const auto it = std::ranges::find_if_not(vec, is_num_token);
const StrVec part1 = std::vector(cbegin(vec), it);
const StrVec part2 = std::vector(it+1, cend(vec));
return {part1, part2};
}
void remove_leading_n_elements_from_each_sub_vector(std::vector<StrVec>& vec, size_t n) {
/// helper function for convert_data()
for (auto& sub_vec : vec) {
sub_vec.erase(begin(sub_vec), begin(sub_vec) + static_cast<int>(n));
}
}
std::vector<Card> convert_data(std::ifstream& data) {
/// Unaltered input data --> vector of cards
/// Each vector element is a card, a card is a pair of {winning nums, your nums}, respectively
// -> [line{strA,strA2,strA3,strA4,...,strAn}, ..., lineN{strN,strN2,strN3,strN4,...,strNn}]
// eg. line: {"84", "73", "|", "24", "20"}
const std::vector<StrVec> tokenized_lines_of_data = [&](){
std::vector<std::string> lines_of_data = vectorize<std::string>(data);
std::vector<StrVec> tokenized_lines_of_data = tokenize(lines_of_data);
remove_leading_n_elements_from_each_sub_vector(tokenized_lines_of_data, 2);
return tokenized_lines_of_data;
}();
// convert data to str cards
// -> [{StrVec0, StrVec'0}, ..., {StrVecn, StrVec'n}]
const std::vector<CardWithStrVals> split_vec = [&tokenized_lines_of_data](){
std::vector<CardWithStrVals> vec;
for (auto& str_vec : tokenized_lines_of_data) {
vec.push_back(split_str_vec(str_vec));
}
return vec;
}();
// convert str cards to `Card`s
// -> [{IntVec0, IntVec'0}, ..., {IntVecn, IntVec'n}]
const std::vector<Card> winning_and_your_numbers = [&](){
std::vector<Card> w;
for (const auto& str_vec_pair : split_vec) {
w.emplace_back(str_to_int_vec(str_vec_pair.first), str_to_int_vec(str_vec_pair.second));
}
return w;
}();
return winning_and_your_numbers;
}
// calc functions
int calc_card_score(int no_of_matches) {
if (no_of_matches == 0) return 0;
return 1 << (no_of_matches-1); // == 1 ^ no_of_matches
}
int calc_card_matches(const Card& card) {
const IntVec& winning_nums = card.first;
const IntVec& your_nums = card.second;
const int matches = std::accumulate(cbegin(winning_nums), cend(winning_nums), 0, [&](int acc, const auto w_num){
return acc + std::any_of(cbegin(your_nums), cend(your_nums), [&](int y_num){ return y_num == w_num; });
});
return matches;
}
int calc_total_score(const std::vector<Card>& winning_and_your_numbers) {
const auto& wayn = winning_and_your_numbers;
return std::accumulate(cbegin(wayn), cend(wayn), 0, [&](int acc, const auto& card){
return acc + calc_card_score(calc_card_matches(card));
});
}
std::map<size_t, int> calc_win_map(const std::vector<Card>& cards) {
std::map<size_t, int> iw;
for (size_t i = 0; i < cards.size(); ++i) {
iw[i] = calc_card_matches(cards[i]);
}
return iw;
}
std::vector<int> calc_card_count(const std::vector<int>& initial_count, const std::map<size_t, int>& wins_on_card) {
assert(wins_on_card.size() == initial_count.size());
std::vector<int> card_count = initial_count;
for (size_t i = 0; i < card_count.size(); ++i) {
const auto no_of_matches = wins_on_card.at(i);
for (size_t m = 1; m <= no_of_matches; ++m) {
if (i+m >= card_count.size()) continue;
card_count[i+m] += card_count[i];
}
}
return card_count;
}
int main() {
std::ifstream data("../data.txt");
if (!data.is_open()) {
throw std::runtime_error("Unable to open file");
}
const std::vector<Card> cards = convert_data(data);
const std::map<size_t, int> wins_on_card = calc_win_map(cards);
const std::vector<int> card_count = [&](){
const auto initial_count = std::vector<int>(wins_on_card.size(), 1);
return calc_card_count(initial_count, wins_on_card);
}();
const int part_1 = calc_total_score(cards);
const int part_2 = std::accumulate(cbegin(card_count), cend(card_count), 0);
fmt::print("Part 1: {}\n", part_1);
fmt::print("Part 2: {}\n", part_2);
}