Skip to content

Commit ab8c845

Browse files
committed
adventofcode/cc: add solution for 2022/23.
This was kind of fun, a slightly easier problem than most days surrounding it. I don't have the energy to try to make it faster right now. Benchmarks from `./bazel run -c opt //adventofcode/cc/year2022:day23_benchmark`: 2023-05-29T15:28:37+01:00 Running /home/saser/.cache/bazel/_bazel_saser/06ad534583e887506ca6aa175f8ed7b5/execroot/code/bazel-out/k8-opt/bin/adventofcode/cc/year2022/day23_benchmark Run on (16 X 4679.3 MHz CPU s) CPU Caches: L1 Data 32 KiB (x16) L1 Instruction 32 KiB (x8) L2 Unified 512 KiB (x8) L3 Unified 16384 KiB (x1) Load Average: 0.22, 0.52, 0.65 ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead. ----------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------- BM_Part1/day23.real.in 2364293 ns 2364297 ns 296 BM_Part2/day23.real.in 440200568 ns 440198290 ns 2
1 parent 8b5d2ca commit ab8c845

File tree

8 files changed

+353
-0
lines changed

8 files changed

+353
-0
lines changed

adventofcode/cc/year2022/BUILD.bazel

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,44 @@ cc_aoc_benchmark(
743743
},
744744
)
745745

746+
cc_library(
747+
name = "day23",
748+
srcs = ["day23.cc"],
749+
hdrs = ["day23.h"],
750+
deps = [
751+
"@com_google_absl//absl/container:flat_hash_map",
752+
"@com_google_absl//absl/container:flat_hash_set",
753+
"@com_google_absl//absl/status",
754+
"@com_google_absl//absl/status:statusor",
755+
"@com_google_absl//absl/strings",
756+
"@com_google_absl//absl/strings:str_format",
757+
],
758+
)
759+
760+
cc_aoc_test(
761+
name = "day23_test",
762+
library = ":day23",
763+
part1 = {
764+
"//adventofcode/data/year2022:day23.example.in": "//adventofcode/data/year2022:day23.example.part1.out",
765+
"//adventofcode/data/year2022:day23.real.in": "//adventofcode/data/year2022:day23.real.part1.out",
766+
},
767+
part2 = {
768+
"//adventofcode/data/year2022:day23.example.in": "//adventofcode/data/year2022:day23.example.part2.out",
769+
"//adventofcode/data/year2022:day23.real.in": "//adventofcode/data/year2022:day23.real.part2.out",
770+
},
771+
)
772+
773+
cc_aoc_benchmark(
774+
name = "day23_benchmark",
775+
library = ":day23",
776+
part1 = {
777+
"//adventofcode/data/year2022:day23.real.in": "//adventofcode/data/year2022:day23.real.part1.out",
778+
},
779+
part2 = {
780+
"//adventofcode/data/year2022:day23.real.in": "//adventofcode/data/year2022:day23.real.part2.out",
781+
},
782+
)
783+
746784
cc_library(
747785
name = "day25",
748786
srcs = ["day25.cc"],

adventofcode/cc/year2022/day23.cc

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
#include "adventofcode/cc/year2022/day23.h"
2+
3+
#include <algorithm>
4+
#include <optional>
5+
#include <sstream>
6+
#include <string>
7+
#include <tuple>
8+
#include <vector>
9+
10+
#include "absl/container/flat_hash_map.h"
11+
#include "absl/container/flat_hash_set.h"
12+
#include "absl/status/status.h"
13+
#include "absl/status/statusor.h"
14+
#include "absl/strings/str_format.h"
15+
#include "absl/strings/str_split.h"
16+
#include "absl/strings/string_view.h"
17+
18+
namespace adventofcode {
19+
namespace cc {
20+
namespace year2022 {
21+
namespace day23 {
22+
namespace {
23+
24+
struct Pos {
25+
int x; // Increases from left to right.
26+
int y; // Increases from top to bottom.
27+
28+
std::string String() const { return absl::StrFormat("(%d,%d)", x, y); }
29+
30+
// clangd complains that this is unused, but it is actually required by the
31+
// Abseil hashing stuff below, hence why the linter is suppressed here.
32+
// NOLINTNEXTLINE
33+
friend inline bool operator==(const Pos& lhs, const Pos& rhs) {
34+
return lhs.x == rhs.x && lhs.y == rhs.y;
35+
}
36+
37+
template <typename H>
38+
friend H AbslHashValue(H h, const Pos& p) {
39+
return H::combine(std::move(h), p.x, p.y);
40+
}
41+
};
42+
43+
constexpr inline Pos NW{.x = -1, .y = -1};
44+
constexpr inline Pos N{.x = 0, .y = -1};
45+
constexpr inline Pos NE{.x = +1, .y = -1};
46+
constexpr inline Pos E{.x = +1, .y = 0};
47+
constexpr inline Pos W{.x = -1, .y = 0};
48+
constexpr inline Pos SW{.x = -1, .y = +1};
49+
constexpr inline Pos S{.x = 0, .y = +1};
50+
constexpr inline Pos SE{.x = +1, .y = +1};
51+
52+
class Elves {
53+
public:
54+
static Elves Parse(absl::string_view input) {
55+
Elves e;
56+
int y = 0;
57+
for (absl::string_view line :
58+
absl::StrSplit(input, '\n', absl::SkipEmpty())) {
59+
for (size_t x = 0; x < line.size(); x++) {
60+
if (line[x] == '#') {
61+
e.occupied_.insert(Pos{.x = int(x), .y = y});
62+
}
63+
}
64+
y++;
65+
}
66+
return e;
67+
}
68+
69+
std::string String() const {
70+
std::stringstream s;
71+
const auto [min_x, max_x, min_y, max_y] = Bounds();
72+
for (int y = min_y; y <= max_y; y++) {
73+
for (int x = min_x; x <= max_x; x++) {
74+
s << (occupied_.contains(Pos{.x = x, .y = y}) ? '#' : '.');
75+
}
76+
if (y < max_y) {
77+
s << std::endl;
78+
}
79+
}
80+
return s.str();
81+
}
82+
83+
// Perform one round and return whether any Elf moved.
84+
bool Round() {
85+
// Collect a list of moves. The mapping is from destination to a list of
86+
// sources. The reason is that several Elves can propose to move to the same
87+
// destination, and in that case none of them should move.
88+
absl::flat_hash_map<Pos, std::vector<Pos>> moves;
89+
for (const Pos& src : occupied_) {
90+
std::vector<Pos> proposals;
91+
for (char dir : directions_) {
92+
std::optional<Pos> dst;
93+
switch (dir) {
94+
case 'N':
95+
dst = ProposeNorth(src);
96+
break;
97+
case 'S':
98+
dst = ProposeSouth(src);
99+
break;
100+
case 'W':
101+
dst = ProposeWest(src);
102+
break;
103+
case 'E':
104+
dst = ProposeEast(src);
105+
break;
106+
}
107+
if (dst.has_value()) {
108+
proposals.push_back(dst.value());
109+
}
110+
}
111+
// If _all_ directions are possible, it means the Elf is surrounded by
112+
// empty space. If _no_ directions are possible, it means the Elf cannot
113+
// move anywhere. In both of these cases, the Elf should do nothing.
114+
if (size_t n = proposals.size(); n == 0 || n == 4) {
115+
continue;
116+
}
117+
// Otherwise, the Elf is able to move. The list of proposed new positions
118+
// is already ordered according to preference, so we take the first one.
119+
moves[proposals.front()].push_back(src);
120+
}
121+
// Once all moves have been collected, perform them all at once.
122+
bool any_moved = false;
123+
for (const auto& [dst, srcs] : moves) {
124+
// If more than one Elf has proposed this move, skip it.
125+
if (srcs.size() > 1) {
126+
continue;
127+
}
128+
occupied_.erase(srcs.front());
129+
occupied_.insert(dst);
130+
any_moved = true;
131+
}
132+
// Rotate the directions by removing the first element and putting it at the
133+
// back.
134+
char dir = directions_.front();
135+
directions_.erase(directions_.begin());
136+
directions_.push_back(dir);
137+
return any_moved;
138+
}
139+
140+
int EmptyGround() const {
141+
const auto [min_x, max_x, min_y, max_y] = Bounds();
142+
int width = max_x - min_x + 1;
143+
int height = max_y - min_y + 1;
144+
return width * height - occupied_.size();
145+
}
146+
147+
private:
148+
Elves() : occupied_(), directions_({'N', 'S', 'W', 'E'}){};
149+
150+
std::tuple<int, int, int, int> Bounds() const {
151+
auto cmp_x = [](const Pos& p1, const Pos& p2) -> bool {
152+
return p1.x < p2.x;
153+
};
154+
auto cmp_y = [](const Pos& p1, const Pos& p2) -> bool {
155+
return p1.y < p2.y;
156+
};
157+
int min_x =
158+
std::min_element(occupied_.cbegin(), occupied_.cend(), cmp_x)->x;
159+
int max_x =
160+
std::max_element(occupied_.cbegin(), occupied_.cend(), cmp_x)->x;
161+
int min_y =
162+
std::min_element(occupied_.cbegin(), occupied_.cend(), cmp_y)->y;
163+
int max_y =
164+
std::max_element(occupied_.cbegin(), occupied_.cend(), cmp_y)->y;
165+
return {min_x, max_x, min_y, max_y};
166+
}
167+
168+
std::optional<Pos> ProposeNorth(const Pos& p) const {
169+
for (const Pos& d : {NW, N, NE}) {
170+
if (occupied_.contains(Pos{.x = p.x + d.x, .y = p.y + d.y})) {
171+
return std::nullopt;
172+
}
173+
}
174+
return Pos{.x = p.x + N.x, .y = p.y + N.y};
175+
}
176+
177+
std::optional<Pos> ProposeSouth(const Pos& p) const {
178+
for (const Pos& d : {SW, S, SE}) {
179+
if (occupied_.contains(Pos{.x = p.x + d.x, .y = p.y + d.y})) {
180+
return std::nullopt;
181+
}
182+
}
183+
return Pos{.x = p.x + S.x, .y = p.y + S.y};
184+
}
185+
186+
std::optional<Pos> ProposeWest(const Pos& p) const {
187+
for (const Pos& d : {W, NW, SW}) {
188+
if (occupied_.contains(Pos{.x = p.x + d.x, .y = p.y + d.y})) {
189+
return std::nullopt;
190+
}
191+
}
192+
return Pos{.x = p.x + W.x, .y = p.y + W.y};
193+
}
194+
195+
std::optional<Pos> ProposeEast(const Pos& p) const {
196+
for (const Pos& d : {E, NE, SE}) {
197+
if (occupied_.contains(Pos{.x = p.x + d.x, .y = p.y + d.y})) {
198+
return std::nullopt;
199+
}
200+
}
201+
return Pos{.x = p.x + E.x, .y = p.y + E.y};
202+
}
203+
204+
absl::flat_hash_set<Pos> occupied_;
205+
std::vector<char> directions_;
206+
};
207+
208+
absl::StatusOr<std::string> solve(absl::string_view input, bool part1) {
209+
Elves e = Elves::Parse(input);
210+
if (part1) {
211+
for (int i = 0; i < 10; i++) {
212+
e.Round();
213+
}
214+
return std::to_string(e.EmptyGround());
215+
}
216+
int rounds = 1;
217+
while (e.Round()) {
218+
rounds++;
219+
}
220+
return std::to_string(rounds);
221+
}
222+
} // namespace
223+
224+
absl::StatusOr<std::string> Part1(absl::string_view input) {
225+
return solve(input, /*part1=*/true);
226+
}
227+
228+
absl::StatusOr<std::string> Part2(absl::string_view input) {
229+
return solve(input, /*part1=*/false);
230+
}
231+
} // namespace day23
232+
} // namespace year2022
233+
} // namespace cc
234+
} // namespace adventofcode
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
....#..
2+
..###.#
3+
#...#.#
4+
.#...##
5+
#.###..
6+
##.#.##
7+
.#..#..
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
110
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
20
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
#.####..#...#.###.#...#.######..###.####.#....##..#..##.###.#..#......
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3788
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
921

0 commit comments

Comments
 (0)