-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday05.cpp
More file actions
234 lines (193 loc) · 8.26 KB
/
day05.cpp
File metadata and controls
234 lines (193 loc) · 8.26 KB
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include "day05.h"
#include "helpers.h"
#include <array>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
namespace day05
{
long long solvePart1(std::ifstream& file);
long long solvePart2(std::ifstream& file);
void run_day(bool example)
{
std::cout << "Running day 05 " << (example ? "(example)" : "") << '\n';
const std::string fileName{ example ? "inputs/day05_example.txt" : "inputs/day05_real.txt" };
std::ifstream file{ fileName };
std::cout << "Part 1 answer: " << solvePart1(file) << '\n';
file.close();
file.open(fileName);
std::cout << "Part 2 answer: " << solvePart2(file) << '\n';
}
struct range
{
long long start{};
long long end{};
};
struct rangeMapping
{
range source{};
long long offSet{};
};
// Parses the mapping section of input to a list of mapping sections,
// each mapping section contains a list of range-mappings, where each range-mapping
// consists of a source range and an offset by which mapping happens.
std::vector<std::vector<rangeMapping>> parseMappings(std::ifstream& file)
{
std::string line;
std::vector<std::vector<rangeMapping>> mappings(0);
while (!file.eof())
{
// Skip over non-number lines till we get to next mapping section numbers
do
{
std::getline(file, line);
} while (!std::isdigit(line[0]));
// Create a list of mapping ranges:
std::vector<rangeMapping> mappingRangesOfMapping(0);
while (std::isdigit(line[0]))
{
long long targetStart;
long long sourceStart;
long long length;
std::stringstream lineStream;
lineStream << line;
lineStream >> targetStart;
lineStream >> sourceStart;
lineStream >> length;
const range sourceRange{ sourceStart, sourceStart + length };
const rangeMapping mapping{ sourceRange, targetStart - sourceStart };
mappingRangesOfMapping.push_back(mapping);
if (file.eof())
{
break;
}
std::getline(file, line);
}
mappings.push_back(mappingRangesOfMapping);
}
return mappings;
}
// Parses input into a pair, first of which is list of seed numbers, second is a list
// of mappings, where each mapping is a a list of mapping-ranges, which consist of three integers
// (start target, start source, length) each.
std::pair<std::vector<long long>, std::vector<std::vector<rangeMapping>>> parseInputPart1(std::ifstream& file)
{
// First parse the seeds
std::string line;
std::getline(file, line);
const size_t breakPos = line.find(' ');
std::vector<long long> seeds{
parseLineOfNumbersToLongLong(line.substr(breakPos, line.size() - breakPos)) };
// Then parse the mappings
auto mappings{ parseMappings(file) };
return { seeds, mappings };
}
// Parses input into a pair, first of which is list of seed ranges, second is a list
// of mappings, where each mapping is a a list of mapping-ranges, which consist of three integers
// (start target, start source, length) each.
std::pair<std::vector<range>, std::vector<std::vector<rangeMapping>>> parseInputPart2(std::ifstream& file)
{
// First parse the seeds
std::string line;
std::getline(file, line);
const size_t breakPos = line.find(' ');
const auto seedInputs{
parseLineOfNumbersToLongLong(line.substr(breakPos, line.size() - breakPos)) };
std::vector<range> seedRanges;
const size_t numberOfRanges = seedInputs.size() / 2;
for (size_t i = 0; i < numberOfRanges; i++)
{
// Each two numbers form a seedrange together;
range seedRange{ seedInputs[i * 2], seedInputs[i * 2] + seedInputs[i * 2 + 1] };
seedRanges.push_back(seedRange);
}
// Then parse the mappings
auto mappings{ parseMappings(file) };
return { seedRanges, mappings };
}
long long solvePart1(std::ifstream& file)
{
// Parse the input
auto [seeds, mappings] = parseInputPart1(file);
// loop over each step in the 'mapping' proces
for (auto& fullMapping : mappings)
{
// For each mapping update each seed
for (auto& value : seeds)
{
// Check each range for the correct one
for (const auto& mappingRange : fullMapping)
{
if (value > mappingRange.source.start && value < mappingRange.source.end)
{
// Update the seed by the difference in start of range.
value += mappingRange.offSet;
break;
}
}
}
}
// Have final mappings of each seed now, just take min
return std::ranges::min(seeds);
}
long long solvePart2(std::ifstream& file)
{
// Parse the input
auto [seedRanges, mappings] = parseInputPart2(file);
// loop over each step in the 'mapping' proces
for (auto& fullMapping : mappings)
{
// Keep track both of mapped new ranges, and remainder of current ranges
std::vector<range> newRanges(0);
std::vector<range> remainingRanges = seedRanges;
// Loop over the range-mappings of the mapping section
for (auto& mappingRange : fullMapping)
{
std::vector<range> newRemainingRanges(0);
// Apply to each current remaing range
for(auto& remainingRange: remainingRanges)
{
// The result of applying mapping to seedRange can be seen as three outputs:
// 1. the part of seedrange below start of source-start (remains)
// 2. the part of seedrange between source-start and source-end (shifted)
// 3. the part of seedrange above source-end (remains)
range section1{ std::min(remainingRange.start, mappingRange.source.start),
std::min(remainingRange.end, mappingRange.source.start) };
if (section1.end - section1.start > 0)
{
newRemainingRanges.push_back(section1);
}
range section2{ std::max(remainingRange.start, mappingRange.source.start),
std::min(remainingRange.end, mappingRange.source.end) };
if (section2.end - section2.start > 0)
{
range mappedSection2{
section2.start + mappingRange.offSet, section2.end + mappingRange.offSet };
newRanges.push_back(mappedSection2);
}
range section3{ std::max(remainingRange.start, mappingRange.source.end),
std::max(remainingRange.end, mappingRange.source.end) };
if (section3.end - section3.start)
{
newRemainingRanges.push_back(section3);
}
}
// The newly remaining ranges after 'cutting out' parts with the current mapping.
remainingRanges = newRemainingRanges;
}
// Add any remainders after all 'cutting out' to new ranges for next mapping section:
for (range remainingRange : remainingRanges)
{
newRanges.push_back(remainingRange);
}
// Replace seedRanges with the results of the current mapping section.
seedRanges = newRanges;
}
// Have final mappings of each seed now, just take min
// Min here is lowest start of a range.
auto minOfRanges = [](range a, range b) { return (a.start < b.start); };
return std::ranges::min(seedRanges, minOfRanges).start;
}
}