Skip to content

Commit 587dc1a

Browse files
committed
2024 day 14: solve part 2
1 parent d547c50 commit 587dc1a

File tree

2 files changed

+109
-22
lines changed

2 files changed

+109
-22
lines changed

2024/src/day14.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,33 @@
99
#include "lib.hpp"
1010
#include <fstream> // for ifstream
1111
#include <iostream> // for cout
12+
#include <string> // for string
1213

1314
int main(int argc, char **argv) {
1415
std::ifstream infile = aoc::parse_args(argc, argv);
1516
const bool is_example =
1617
std::string{argv[1]}.find("example") != std::string::npos;
1718
aoc::Pos bounds = is_example ? aoc::Pos{11, 7} : aoc::Pos{101, 103};
1819

19-
auto robots = aoc::day14::read_input(infile);
20+
auto robots = aoc::day14::Robots::read(infile, bounds);
2021

21-
for (int time = 0; time < 100; ++time) {
22-
for (auto &robot : robots) {
23-
robot.update(bounds);
22+
for (int time = 0; time < 10000; ++time) {
23+
if (time == 100) {
24+
// part 1
25+
std::cout << robots.safety_factor() << "\n";
26+
if (is_example) {
27+
break;
28+
}
29+
}
30+
31+
if (robots.largest_clump() > 10) {
32+
// part 2
33+
std::cout << time << "\n";
34+
break;
2435
}
25-
}
2636

27-
std::cout << aoc::day14::safety_factor(robots, bounds) << "\n";
37+
robots.update();
38+
}
2839

2940
return 0;
3041
}

2024/src/day14.hpp

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
#ifndef DAY14_HPP_5W9MWUPG
99
#define DAY14_HPP_5W9MWUPG
1010

11-
#include "lib.hpp" // for Pos, Delta, expect_input
12-
#include <array> // for array
13-
#include <iostream> // for istream
14-
#include <string> // for string, getline
15-
#include <utility> // for move
16-
#include <vector> // for vector
11+
#include "ds/grid.hpp" // for Grid
12+
#include "lib.hpp" // for Pos, Delta, expect_input, DIRECTIONS
13+
#include <algorithm> // for max_element
14+
#include <array> // for array
15+
#include <iostream> // for istream, ostream, noskipws, ws
16+
#include <utility> // for move
17+
#include <vector> // for vector
18+
// IWYU pragma: no_include <initializer_list> // for DIRECTIONS
1719

1820
namespace aoc::day14 {
1921

@@ -33,15 +35,6 @@ std::istream &operator>>(std::istream &is, Robot &robot) {
3335
return is;
3436
}
3537

36-
auto read_input(std::istream &is) {
37-
Robot robot;
38-
std::vector<Robot> robots;
39-
while (is >> robot) {
40-
robots.push_back(std::move(robot));
41-
}
42-
return robots;
43-
}
44-
4538
void Robot::update(const Pos &bounds) {
4639
pos += vel;
4740
if (pos.x < 0) {
@@ -56,7 +49,43 @@ void Robot::update(const Pos &bounds) {
5649
}
5750
}
5851

59-
int safety_factor(const std::vector<Robot> &robots, const Pos &bounds) {
52+
class Robots {
53+
std::vector<Robot> robots;
54+
Pos bounds;
55+
56+
aoc::ds::Grid<int> robot_counts;
57+
58+
explicit Robots(const Pos &bounds)
59+
: robots(), bounds(bounds), robot_counts(bounds.x, bounds.y, 0) {}
60+
61+
public:
62+
static Robots read(std::istream &is, const Pos &bounds);
63+
void update();
64+
int safety_factor() const;
65+
int largest_clump() const;
66+
67+
friend std::ostream &operator<<(std::ostream &os, const Robots &robots);
68+
};
69+
70+
Robots Robots::read(std::istream &is, const Pos &bounds) {
71+
Robot robot;
72+
Robots robots(bounds);
73+
while (is >> robot) {
74+
++robots.robot_counts[robot.pos];
75+
robots.robots.push_back(std::move(robot));
76+
}
77+
return robots;
78+
}
79+
80+
void Robots::update() {
81+
for (auto &robot : robots) {
82+
--robot_counts[robot.pos];
83+
robot.update(bounds);
84+
++robot_counts[robot.pos];
85+
}
86+
}
87+
88+
int Robots::safety_factor() const {
6089
std::array<int, 4> quadrants{};
6190
for (const auto &robot : robots) {
6291
int quad_idx = 0;
@@ -77,6 +106,53 @@ int safety_factor(const std::vector<Robot> &robots, const Pos &bounds) {
77106
return quadrants[0] * quadrants[1] * quadrants[2] * quadrants[3];
78107
}
79108

109+
int Robots::largest_clump() const {
110+
std::vector<int> clumps;
111+
aoc::ds::Grid<int> clump_indices(robot_counts, -1);
112+
for (const Robot &robot : robots) {
113+
// This isn't actually correct, and mislabels shapes with indents like
114+
// ..##
115+
// .##.
116+
// but it's good enough to find a Christmas tree. See day 12 for a
117+
// proper (and slower) implementation.
118+
if (clump_indices[robot.pos] == -1) {
119+
for (const auto &dir : DIRECTIONS) {
120+
Pos neighbor = robot.pos + aoc::Delta(dir, true);
121+
if (robot_counts.in_bounds(neighbor) &&
122+
robot_counts[neighbor] > 0) {
123+
if (clump_indices[neighbor] != -1) {
124+
clump_indices[robot.pos] = clump_indices[neighbor];
125+
++clumps[clump_indices[robot.pos]];
126+
break;
127+
}
128+
}
129+
}
130+
if (clump_indices[robot.pos] == -1) {
131+
// new clump
132+
clump_indices[robot.pos] = clumps.size();
133+
clumps.push_back(1);
134+
}
135+
}
136+
}
137+
return *std::max_element(clumps.begin(), clumps.end());
138+
}
139+
140+
std::ostream &operator<<(std::ostream &os, const Robots &robots) {
141+
for (const auto &row : robots.robot_counts) {
142+
for (const auto &count : row) {
143+
if (count == 0) {
144+
os << ' ';
145+
} else if (count < 10) {
146+
os << count;
147+
} else {
148+
os << '*';
149+
}
150+
}
151+
os << "\n";
152+
}
153+
return os;
154+
}
155+
80156
} // namespace aoc::day14
81157

82158
#endif /* end of include guard: DAY14_HPP_5W9MWUPG */

0 commit comments

Comments
 (0)