Skip to content

Commit 5a738c5

Browse files
committed
Solve Day 10 Part 1
1 parent 0adb9e4 commit 5a738c5

File tree

4 files changed

+283
-1
lines changed

4 files changed

+283
-1
lines changed

.DS_Store

6 KB
Binary file not shown.

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ if(ipo_available AND (NOT CMAKE_BUILD_TYPE MATCHES Debug) AND (NOT CMAKE_BUILD_T
2929
set_property(TARGET aocio PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
3030
endif()
3131

32-
set(TARGETS day-01 day-02 day-03 day-04 day-05 day-06 day-07 day-08 day-09) # Add the other days as you please.
32+
set(TARGETS day-01 day-02 day-03 day-04 day-05 day-06 day-07 day-08 day-09 day-10) # Add the other days as you please.
3333

3434
list(LENGTH TARGETS NUM_TARGETS)
3535

@@ -66,5 +66,7 @@ add_custom_target("run-all"
6666
COMMAND day-07
6767
COMMAND day-08
6868
COMMAND day-09
69+
COMMAND day-10
70+
6971
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin
7072
)

day-10/day-10.cpp

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
#include <string>
2+
#include <unordered_map>
3+
#include <numeric>
4+
#include <array>
5+
#include <queue>
6+
#include "../aocio/aocio.hpp"
7+
8+
/*
9+
Problem: https://adventofcode.com/2023/day/10
10+
11+
Solutions:
12+
- Part 1: 6812
13+
- Part 2:
14+
Notes:
15+
16+
*/
17+
18+
struct GridPos {
19+
int x, y;
20+
};
21+
22+
enum class Direction {North=0, East=1, South=2, West=3};
23+
const std::array<Direction,4> all_dirs = {Direction::North, Direction::East, Direction::South, Direction::West};
24+
25+
const std::unordered_map<char, std::pair<Direction, Direction>> pipe_to_dir {
26+
{'|', {Direction::North, Direction::South}},
27+
{'-', {Direction::East, Direction::West}},
28+
{'L', {Direction::North, Direction::East}},
29+
{'J', {Direction::North, Direction::West}},
30+
{'7', {Direction::South, Direction::West}},
31+
{'F', {Direction::South, Direction::East}},
32+
};
33+
34+
Direction dir_opposite(Direction d)
35+
{
36+
switch (d) {
37+
case Direction::North:
38+
return Direction::South;
39+
case Direction::South:
40+
return Direction::North;
41+
case Direction::East:
42+
return Direction::West;
43+
case Direction::West:
44+
return Direction::East;
45+
default:
46+
throw "Invalid direction";
47+
}
48+
}
49+
50+
bool pipes_connect(char p1, char p2, Direction p1_rel_dir)
51+
{
52+
if (p1 == 'S' && p2 == 'S') {
53+
throw "Two start vertices";
54+
}
55+
if (p1 == '.' || p2 == '.') {
56+
return false;
57+
}
58+
59+
std::optional<std::pair<Direction, Direction>> p1_dir {};
60+
std::optional<std::pair<Direction, Direction>> p2_dir {};
61+
if (pipe_to_dir.contains(p1)) {
62+
p1_dir = pipe_to_dir.at(p1);
63+
} else if (p1 != 'S') {
64+
throw "Invalid pipe symbol";
65+
}
66+
67+
if (pipe_to_dir.contains(p2)) {
68+
p2_dir = pipe_to_dir.at(p2);
69+
} else if (p2 != 'S') {
70+
throw "Invalid pipe symbol";
71+
}
72+
73+
if (!p1_dir && !p2_dir) {
74+
throw "Invalid pipe symbol";
75+
}
76+
77+
if (p2_dir) {
78+
if (p2_dir.value().first != p1_rel_dir && p2_dir.value().second != p1_rel_dir) {
79+
return false;
80+
}
81+
} else {
82+
assert(p1_dir);
83+
if (p1_dir.value().first != dir_opposite(p1_rel_dir) && p1_dir.value().second != dir_opposite(p1_rel_dir)) {
84+
return false;
85+
}
86+
}
87+
88+
if (p1 == 'S' || p2 == 'S') {
89+
return true;
90+
}
91+
92+
if (!p1_dir || !p2_dir) {
93+
throw "Should not happen";
94+
}
95+
96+
return dir_opposite(p1_dir.value().first) == (p2_dir.value().first) || dir_opposite(p1_dir.value().first) == (p2_dir.value().second) ||
97+
dir_opposite(p1_dir.value().second) == (p2_dir.value().first) || dir_opposite(p1_dir.value().second) == p2_dir.value().second;
98+
}
99+
100+
GridPos vertname_to_gridpos(const std::string& vert)
101+
{
102+
auto sep_idx = vert.find(",");
103+
assert(sep_idx != std::string::npos);
104+
assert(sep_idx < vert.size() - 1);
105+
int x = aocio::parse_num(vert.substr(0, sep_idx));
106+
int y = aocio::parse_num(vert.substr(sep_idx + 1, vert.size()));
107+
return GridPos {x, y};
108+
}
109+
110+
std::string gridpos_to_vertname(const GridPos& pos)
111+
{
112+
std::string vert;
113+
vert = std::to_string(pos.x) + "," + std::to_string(pos.y);
114+
return vert;
115+
}
116+
117+
struct Graph {
118+
std::string start_vert {};
119+
std::vector<std::string> grid {};
120+
std::unordered_map<std::string, int> loop_verts_dist {};
121+
122+
GridPos start_pos()
123+
{
124+
return vertname_to_gridpos(start_vert);
125+
}
126+
127+
char grid_get_sym(const GridPos& pos)
128+
{
129+
return grid.at(pos.y).at(pos.x);
130+
}
131+
132+
bool pos_on_grid(const GridPos &pos)
133+
{
134+
bool in_y = pos.y >= 0 && pos.y < std::ssize(grid);
135+
if (!in_y) {
136+
return false;
137+
}
138+
bool in_x = pos.x >= 0 && pos.x < std::ssize(grid.at(pos.y));
139+
return in_x;
140+
}
141+
142+
std::optional<char> grid_peek(const GridPos &pos, Direction d, GridPos &new_pos)
143+
{
144+
GridPos peek_pos = pos;
145+
switch (d)
146+
{
147+
case Direction::North:
148+
peek_pos.y -= 1;
149+
break;
150+
case Direction::East:
151+
peek_pos.x += 1;
152+
break;
153+
case Direction::South:
154+
peek_pos.y += 1;
155+
break;
156+
case Direction::West:
157+
peek_pos.x -= 1;
158+
break;
159+
default:
160+
throw "Invalid direction";
161+
break;
162+
}
163+
164+
if (pos_on_grid(peek_pos)) {
165+
new_pos = peek_pos;
166+
return grid_get_sym(peek_pos);
167+
} else {
168+
new_pos = pos;
169+
return {};
170+
}
171+
}
172+
173+
void find_adjacent(const GridPos& pos, std::vector<GridPos>& neighbors)
174+
{
175+
char grid_sym = grid_get_sym(pos);
176+
for (Direction d : all_dirs) {
177+
GridPos peek_pos;
178+
auto neighbor_sym = grid_peek(pos, d, peek_pos);
179+
if (neighbor_sym && pipe_to_dir.contains(neighbor_sym.value()) && pipes_connect(neighbor_sym.value(), grid_sym, d)) {
180+
neighbors.push_back(peek_pos);
181+
}
182+
}
183+
assert(neighbors.size() <= 2);
184+
}
185+
186+
int find_loop_verts()
187+
{
188+
// Simple breadth-first-search of the cyclical graph.
189+
loop_verts_dist.insert({start_vert, 0});
190+
std::queue<std::string> vert_queue;
191+
vert_queue.push(start_vert);
192+
193+
while (!vert_queue.empty()) {
194+
std::string vert = vert_queue.front();
195+
vert_queue.pop();
196+
197+
int dist = loop_verts_dist.at(vert);
198+
199+
std::vector<GridPos> neighbors;
200+
find_adjacent(vertname_to_gridpos(vert), neighbors);
201+
for (const auto& pos : neighbors) {
202+
std::string vert_name = gridpos_to_vertname(pos);
203+
if (loop_verts_dist.contains(vert_name)) { // Already visited.
204+
continue;
205+
} else {
206+
loop_verts_dist.insert({vert_name, dist + 1});
207+
vert_queue.push(vert_name);
208+
}
209+
}
210+
}
211+
auto max_dist_it = std::max_element(loop_verts_dist.begin(), loop_verts_dist.end(), [](const auto & a, const auto& b) -> bool {return a.second < b.second;} );
212+
if (max_dist_it == loop_verts_dist.end()) {
213+
throw "Could not find max dist";
214+
}
215+
return max_dist_it->second;
216+
}
217+
};
218+
219+
void parse_grid(const std::vector<std::string>& lines, Graph &result)
220+
{
221+
std::string start_vert {""};
222+
int y = 0;
223+
for (const std::string &line : lines) {
224+
if (!line.size()) {
225+
++y;
226+
continue;
227+
}
228+
auto idx = line.find('S');
229+
if (idx != std::string::npos) {
230+
assert(start_vert == "");
231+
int x = static_cast<int>(idx);
232+
start_vert = gridpos_to_vertname(GridPos{x, y});
233+
}
234+
result.grid.push_back(line);
235+
++y;
236+
}
237+
assert(start_vert != "");
238+
239+
result.start_vert = start_vert;
240+
}
241+
242+
int part_one(const std::vector<std::string>& lines)
243+
{
244+
Graph g;
245+
parse_grid(lines, g);
246+
int max_dist = g.find_loop_verts();
247+
return max_dist;
248+
}
249+
250+
int part_two(const std::vector<std::string>& lines)
251+
{
252+
return -1;
253+
}
254+
255+
int main()
256+
{
257+
aocio::print_day();
258+
std::vector<std::string> lines;
259+
std::string_view fname = AOC_INPUT_DIR"input.txt";
260+
bool file_loaded = aocio::file_getlines(fname, lines);
261+
if (!file_loaded) {
262+
std::cerr << "Error: " << "File '" << fname << "' not found\n";
263+
return EXIT_FAILURE;
264+
}
265+
try {
266+
int p1 = part_one(lines);
267+
std::cout << "Part 1: " << p1 << "\n";
268+
int p2 = part_two(lines);
269+
std::cout << "Part 2: " << p2 << "\n";
270+
} catch (const char* err) {
271+
std::cerr << "Error: " << err << "\n";
272+
return EXIT_FAILURE;
273+
}
274+
return EXIT_SUCCESS;
275+
}

day-10/input-example.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
..F7.
2+
.FJ|.
3+
SJ.L7
4+
|F--J
5+
LJ...

0 commit comments

Comments
 (0)