Skip to content

Commit 9bed464

Browse files
author
bt
committed
Day 14
1 parent a66fab0 commit 9bed464

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ add_challenge(day9)
2121
add_challenge(day10)
2222
add_challenge(day11)
2323
add_challenge(day12)
24+
add_challenge(day13)
25+
add_challenge(day14)
2426

2527
add_custom_command(TARGET day1
2628
COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/data" "./data"
File renamed without changes.

src/day14.cpp

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
#include "common_headers.hpp"
2+
#include "Position.hpp"
3+
4+
#include <cmath>
5+
#include <regex>
6+
7+
struct RobotInfo final
8+
{
9+
Position position{};
10+
11+
int velocityX{};
12+
int velocityY{};
13+
};
14+
15+
[[nodiscard]] size_t solveFirstPart(const std::vector<RobotInfo>& inputVec)
16+
{
17+
constexpr size_t width{101};
18+
constexpr size_t height{103};
19+
constexpr size_t midVertical{(width / 2) + 1};
20+
constexpr size_t midHorizontal{(height / 2) + 1};
21+
std::array<int, 4> counters{};
22+
std::vector<std::vector<int>> map(height, std::vector<int>(width));
23+
24+
for(const auto& robot : inputVec) {
25+
int64_t newX{robot.position.x + robot.velocityX * 100};
26+
int64_t newY{robot.position.y + robot.velocityY * 100};
27+
if(newX < 0) {
28+
newX = (width - (std::abs(newX) % width)) % width;
29+
}
30+
else {
31+
newX = newX % width;
32+
}
33+
if(newY < 0) {
34+
newY = (height - (std::abs(newY) % height)) % height;
35+
}
36+
else {
37+
newY = newY % height;
38+
}
39+
if(newX != midVertical-1 && newY != midHorizontal-1) {
40+
counters[2 * (newY / midHorizontal) + newX / midVertical]++;
41+
}
42+
map[newY][newX]++;
43+
}
44+
45+
return std::accumulate(counters.begin(), counters.end(), size_t{1}, std::multiplies<>{});
46+
}
47+
48+
[[nodiscard]] double EuclideanDistanceFromOrigin(double x, double y) noexcept
49+
{
50+
return std::sqrt(x * x + y * y);
51+
}
52+
53+
[[nodiscard]] double calculateVariance(const std::vector<std::vector<int>>& matrix) noexcept
54+
{
55+
const size_t midVertical{(matrix[0].size() / 2) + 1};
56+
const size_t midHorizontal{(matrix.size() / 2) + 1};
57+
58+
double positionCount{};
59+
double meanDistance{};
60+
for(size_t i = 0; i < matrix.size(); ++i) {
61+
for(size_t j = 0; j < matrix[0].size(); ++j) {
62+
positionCount += matrix[i][j] != 0;
63+
meanDistance += matrix[i][j] * EuclideanDistanceFromOrigin(i, j);
64+
}
65+
}
66+
meanDistance /= positionCount;
67+
68+
double variance{};
69+
for(size_t i = 0; i < matrix.size(); ++i) {
70+
for(size_t j = 0; j < matrix[0].size(); ++j) {
71+
const auto d{EuclideanDistanceFromOrigin(i, j)};
72+
variance += matrix[i][j] * (d - meanDistance) * (d - meanDistance);
73+
}
74+
}
75+
76+
return variance;
77+
}
78+
79+
80+
[[nodiscard]] size_t solveSecondPart(const std::vector<RobotInfo>& inputVec) {
81+
constexpr size_t width{101};
82+
constexpr size_t height{103};
83+
std::vector<std::vector<int>> map(height, std::vector<int>(width));
84+
85+
std::unordered_map<double, size_t> varianceFreq;
86+
87+
double minVariance{std::numeric_limits<double>::max()};
88+
size_t minVarianceId{};
89+
90+
size_t iteration{};
91+
while(++iteration < 10000) {
92+
map.clear();
93+
map.resize(height, std::vector<int>(width));
94+
95+
for(const auto& robot : inputVec) {
96+
int64_t newX{robot.position.x + robot.velocityX * iteration};
97+
int64_t newY{robot.position.y + robot.velocityY * iteration};
98+
if(newX < 0) {
99+
newX = (width - (std::abs(newX) % width)) % width;
100+
}
101+
else {
102+
newX = newX % width;
103+
}
104+
if(newY < 0) {
105+
newY = (height - (std::abs(newY) % height)) % height;
106+
}
107+
else {
108+
newY = newY % height;
109+
}
110+
map[newY][newX] = 1;
111+
}
112+
const double variance{calculateVariance(map)};
113+
varianceFreq[variance]++;
114+
if(variance < minVariance) {
115+
minVariance = variance;
116+
minVarianceId = iteration;
117+
}
118+
}
119+
120+
return minVarianceId;
121+
}
122+
123+
124+
[[nodiscard]] RobotInfo parseRobotInfo(const std::string& input) {
125+
RobotInfo info;
126+
127+
// Define the regex pattern to match key-value pairs like "p=0,4" or "v=3,-3"
128+
std::regex pattern(R"((\w+)=(-?\d+),(-?\d+))");
129+
std::smatch matches;
130+
131+
// Use an iterator to find all matches in the input string
132+
auto begin = std::sregex_iterator(input.begin(), input.end(), pattern);
133+
auto end = std::sregex_iterator();
134+
135+
for (auto it = begin; it != end; ++it) {
136+
matches = *it;
137+
138+
// Extract key and coordinates
139+
const std::string key = matches[1];
140+
const int x{std::stoi(matches[2])};
141+
const int y{std::stoi(matches[3])};
142+
143+
// Store the parsed values in the map
144+
if(key == "p") {
145+
info.position = Position{x, y};
146+
}
147+
else if(key == "v") {
148+
info.velocityX = x;
149+
info.velocityY = y;
150+
}
151+
else {
152+
throw std::logic_error{"unknown input: " + input};
153+
}
154+
}
155+
return info;
156+
}
157+
158+
void printHelp()
159+
{
160+
std::cerr << "\nUsage:\n"
161+
<< "The program requires 2 args: (part1, part2) and the path to the file."
162+
<< "\nFor example, ./day7 part1 data/day7.txt";
163+
}
164+
165+
int main(int argc, char* argv[])
166+
{
167+
if(argc != 3) {
168+
printHelp();
169+
return 1;
170+
}
171+
172+
std::string_view task{argv[1]};
173+
if(task != "part1" && task != "part2") {
174+
std::cerr << "\nfirst arg can be either `part1` or `part2`\n";
175+
printHelp();
176+
return 1;
177+
}
178+
179+
std::vector<RobotInfo> inputVec;
180+
readInput(argv[2], std::back_inserter(inputVec), parseRobotInfo);
181+
182+
if(task == "part1") {
183+
std::cout << solveFirstPart(inputVec);
184+
}
185+
else {
186+
std::cout << solveSecondPart(inputVec);
187+
}
188+
189+
return 0;
190+
}

0 commit comments

Comments
 (0)