Add solution for "Day 18: RAM Run", part 2
This commit is contained in:
parent
bb1dab33e9
commit
f8f431b61a
@ -132,6 +132,14 @@ For part 1, the solver implements the eight defined instructions and a runtime c
|
|||||||
|
|
||||||
For part 2, the algorithm appends iteratively 3-digit binary numbers in such a way, that the desired output is approached. The required search patterns and mappings were determined manually, and are predefined for the specific given program in code. It is not a generic solution.
|
For part 2, the algorithm appends iteratively 3-digit binary numbers in such a way, that the desired output is approached. The required search patterns and mappings were determined manually, and are predefined for the specific given program in code. It is not a generic solution.
|
||||||
|
|
||||||
|
### Day 18: RAM Run
|
||||||
|
|
||||||
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/18>, :white_check_mark: Solver: [`RamRun.cpp`](src/RamRun.cpp)
|
||||||
|
|
||||||
|
After the solver tracked the first 1024 corrupted memory positions, it constructs a graph of all remaining positions and their adjacencies, and runs Dijkstra's algorithm I already used for [day 16](#day-16-reindeer-maze) to calculate the length of the shortest path to the exit.
|
||||||
|
|
||||||
|
Conveniently, Dijkstra's algorithm also finds the shortest path itself, so after each additional falling byte, the algorithm checks whether it hit the current shortest path and if so, tries to find a new one to the exit. The solution for part two is found once this there is no path any more.
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
* [Alexander Brouwer](https://github.com/Bromvlieg) for getting the project set up with CMake.
|
* [Alexander Brouwer](https://github.com/Bromvlieg) for getting the project set up with CMake.
|
||||||
|
@ -33,8 +33,11 @@ class RamRun
|
|||||||
int maxBytes_;
|
int maxBytes_;
|
||||||
int nBytes_;
|
int nBytes_;
|
||||||
Grid<int> vertexReferences_;
|
Grid<int> vertexReferences_;
|
||||||
|
WeightedEdgeGraph::PathsResult dijkstraResult_;
|
||||||
static constexpr char getUnknownVertexReference();
|
static constexpr char getUnknownVertexReference();
|
||||||
static constexpr char getNoVertexReference();
|
static constexpr char getNoVertexReference();
|
||||||
void buildGraph(WeightedEdgeGraph& graph);
|
bool tryMarkCorrupted(const std::string& line, int& corruptedVertex);
|
||||||
|
WeightedEdgeGraph buildGraph();
|
||||||
int getVertex(WeightedEdgeGraph& graph, const size_t x, const size_t y);
|
int getVertex(WeightedEdgeGraph& graph, const size_t x, const size_t y);
|
||||||
|
void resetVertexReferences();
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,23 @@ class WeightedEdgeGraph
|
|||||||
void addEdge(const int vertex1, const int vertex2, const int weight);
|
void addEdge(const int vertex1, const int vertex2, const int weight);
|
||||||
int getVertexWeight(const int vertex) const;
|
int getVertexWeight(const int vertex) const;
|
||||||
int getEdgeWeight(const int edge) const;
|
int getEdgeWeight(const int edge) const;
|
||||||
std::vector<int> dijkstra(const int source) const;
|
|
||||||
|
struct PathsResult
|
||||||
|
{
|
||||||
|
PathsResult()
|
||||||
|
: distances{}, predecessors{}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
PathsResult(const size_t size, const int initialDistance, const int initialPredecessor)
|
||||||
|
: distances(size, initialDistance), predecessors(size, initialPredecessor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
std::vector<int> distances;
|
||||||
|
std::vector<int> predecessors;
|
||||||
|
};
|
||||||
|
|
||||||
|
PathsResult dijkstra(const int source) const;
|
||||||
|
static constexpr int getInfiniteDistance();
|
||||||
|
|
||||||
struct Incidence
|
struct Incidence
|
||||||
{
|
{
|
||||||
|
@ -35,22 +35,42 @@ const int RamRun::getPuzzleDay() const
|
|||||||
|
|
||||||
void RamRun::processDataLine(const std::string& line)
|
void RamRun::processDataLine(const std::string& line)
|
||||||
{
|
{
|
||||||
if (maxBytes_ > nBytes_++)
|
if (part2 == "")
|
||||||
{
|
{
|
||||||
std::istringstream stream{ line };
|
int corrupted{ -1 };
|
||||||
Point2 position;
|
if (!tryMarkCorrupted(line, corrupted))
|
||||||
char c;
|
|
||||||
if (stream >> position.x >> c >> position.y)
|
|
||||||
{
|
{
|
||||||
vertexReferences_.cell(position) = getNoVertexReference();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxBytes_ == nBytes_)
|
if (maxBytes_ == ++nBytes_)
|
||||||
{
|
{
|
||||||
WeightedEdgeGraph graph{};
|
// Calculates initial distances.
|
||||||
buildGraph(graph);
|
auto graph = buildGraph();
|
||||||
auto distances = graph.dijkstra(vertexReferences_[0][0]);
|
dijkstraResult_ = graph.dijkstra(vertexReferences_[0][0]);
|
||||||
part1 = distances[vertexReferences_[memorySize_ - 1][memorySize_ - 1]];
|
part1 = dijkstraResult_.distances[vertexReferences_[memorySize_ - 1][memorySize_ - 1]];
|
||||||
|
}
|
||||||
|
else if (maxBytes_ < nBytes_)
|
||||||
|
{
|
||||||
|
int pathVertex = vertexReferences_[memorySize_ - 1][memorySize_ - 1];
|
||||||
|
while (pathVertex >= 0 && pathVertex != corrupted)
|
||||||
|
{
|
||||||
|
pathVertex = dijkstraResult_.predecessors[pathVertex];
|
||||||
|
}
|
||||||
|
if (pathVertex == corrupted)
|
||||||
|
{
|
||||||
|
// Calculates new paths and distances after path was interruped by corrupted position, and checks if path
|
||||||
|
// is now blocked.
|
||||||
|
resetVertexReferences();
|
||||||
|
auto graph = buildGraph();
|
||||||
|
dijkstraResult_ = graph.dijkstra(vertexReferences_[0][0]);
|
||||||
|
if (dijkstraResult_.distances[vertexReferences_[memorySize_ - 1][memorySize_ - 1]]
|
||||||
|
== WeightedEdgeGraph::getInfiniteDistance())
|
||||||
|
{
|
||||||
|
// Path is blocked.
|
||||||
|
part2 = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,8 +89,25 @@ constexpr char RamRun::getNoVertexReference()
|
|||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RamRun::buildGraph(WeightedEdgeGraph& graph)
|
// Marks the position extracted from the 'line' as corrupted in the vertex reference grid.
|
||||||
|
bool RamRun::tryMarkCorrupted(const std::string& line, int& corruptedVertex)
|
||||||
{
|
{
|
||||||
|
std::istringstream stream{ line };
|
||||||
|
Point2 position;
|
||||||
|
char c;
|
||||||
|
if (stream >> position.x >> c >> position.y)
|
||||||
|
{
|
||||||
|
corruptedVertex = vertexReferences_.cell(position);
|
||||||
|
vertexReferences_.cell(position) = getNoVertexReference();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
corruptedVertex = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeightedEdgeGraph RamRun::buildGraph()
|
||||||
|
{
|
||||||
|
WeightedEdgeGraph graph;
|
||||||
for (size_t j = 0; j < vertexReferences_.getNRows(); j++)
|
for (size_t j = 0; j < vertexReferences_.getNRows(); j++)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < vertexReferences_.getNColumns(); i++)
|
for (size_t i = 0; i < vertexReferences_.getNColumns(); i++)
|
||||||
@ -95,6 +132,8 @@ void RamRun::buildGraph(WeightedEdgeGraph& graph)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RamRun::getVertex(WeightedEdgeGraph& graph, const size_t x, const size_t y)
|
int RamRun::getVertex(WeightedEdgeGraph& graph, const size_t x, const size_t y)
|
||||||
@ -105,3 +144,17 @@ int RamRun::getVertex(WeightedEdgeGraph& graph, const size_t x, const size_t y)
|
|||||||
}
|
}
|
||||||
return vertexReferences_[y][x];
|
return vertexReferences_[y][x];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RamRun::resetVertexReferences()
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < vertexReferences_.getNRows(); j++)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < vertexReferences_.getNColumns(); i++)
|
||||||
|
{
|
||||||
|
if (vertexReferences_[j][i] != getNoVertexReference())
|
||||||
|
{
|
||||||
|
vertexReferences_[j][i] = getUnknownVertexReference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -45,9 +45,9 @@ void ReindeerMaze::finish()
|
|||||||
auto exit = graph.addVertex(0);
|
auto exit = graph.addVertex(0);
|
||||||
buildPathSegmentGraph(graph, vertexAttachedPositions, entry, exit);
|
buildPathSegmentGraph(graph, vertexAttachedPositions, entry, exit);
|
||||||
|
|
||||||
auto shortestDistances = graph.dijkstra(exit);
|
auto result = graph.dijkstra(exit);
|
||||||
part1 = shortestDistances[entry] / 2;
|
part1 = result.distances[entry] / 2;
|
||||||
part2 = calcShortestPaths(graph, vertexAttachedPositions, entry, exit, shortestDistances);
|
part2 = calcShortestPaths(graph, vertexAttachedPositions, entry, exit, result.distances);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char ReindeerMaze::getStartChar()
|
constexpr char ReindeerMaze::getStartChar()
|
||||||
|
@ -49,13 +49,13 @@ int WeightedEdgeGraph::getEdgeWeight(const int edge) const
|
|||||||
return edgeWeights_[edge];
|
return edgeWeights_[edge];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> WeightedEdgeGraph::dijkstra(const int source) const
|
WeightedEdgeGraph::PathsResult WeightedEdgeGraph::dijkstra(const int source) const
|
||||||
{
|
{
|
||||||
std::vector<int> distances(firstVertexIncidences_.size(), std::numeric_limits<int>::max());
|
PathsResult result(firstVertexIncidences_.size(), getInfiniteDistance(), -1);
|
||||||
auto compare = [&distances](int left, int right) { return distances[left] > distances[right]; };
|
auto compare = [&result](int left, int right) { return result.distances[left] > result.distances[right]; };
|
||||||
std::priority_queue<int, std::vector<int>, decltype(compare)> queue{ compare };
|
std::priority_queue<int, std::vector<int>, decltype(compare)> queue{ compare };
|
||||||
|
|
||||||
distances[source] = 0;
|
result.distances[source] = 0;
|
||||||
queue.push(source);
|
queue.push(source);
|
||||||
|
|
||||||
while (!queue.empty())
|
while (!queue.empty())
|
||||||
@ -65,16 +65,22 @@ std::vector<int> WeightedEdgeGraph::dijkstra(const int source) const
|
|||||||
|
|
||||||
for (auto neighbor = begin(v); neighbor != end(); ++neighbor)
|
for (auto neighbor = begin(v); neighbor != end(); ++neighbor)
|
||||||
{
|
{
|
||||||
int newDistance{ distances[v] + edgeWeights_[neighbor->edge] };
|
int newDistance{ result.distances[v] + edgeWeights_[neighbor->edge] };
|
||||||
if (distances[neighbor->vertex] > newDistance)
|
if (result.distances[neighbor->vertex] > newDistance)
|
||||||
{
|
{
|
||||||
distances[neighbor->vertex] = newDistance;
|
result.distances[neighbor->vertex] = newDistance;
|
||||||
|
result.predecessors[neighbor->vertex] = v;
|
||||||
queue.push(neighbor->vertex);
|
queue.push(neighbor->vertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return distances;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int WeightedEdgeGraph::getInfiniteDistance()
|
||||||
|
{
|
||||||
|
return std::numeric_limits<int>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
WeightedEdgeGraph::NeighborIterator WeightedEdgeGraph::begin(const int vertex) const
|
WeightedEdgeGraph::NeighborIterator WeightedEdgeGraph::begin(const int vertex) const
|
||||||
|
@ -332,11 +332,11 @@ TEST_CASE("[RamRunTests]")
|
|||||||
TestContext test;
|
TestContext test;
|
||||||
SECTION("FullData")
|
SECTION("FullData")
|
||||||
{
|
{
|
||||||
test.run(std::make_unique<RamRun>(), 248, "", test.getInputPaths());
|
test.run(std::make_unique<RamRun>(), 248, "32,55", test.getInputPaths());
|
||||||
}
|
}
|
||||||
SECTION("ExampleData")
|
SECTION("ExampleData")
|
||||||
{
|
{
|
||||||
test.run(std::make_unique<RamRun>(7, 12), 22, "", test.getExampleInputPaths());
|
test.run(std::make_unique<RamRun>(7, 12), 22, "6,1", test.getExampleInputPaths());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user