Change part 1 of day 20 to use a generator
parent
1b5fcd12f7
commit
1601cfebbf
262
day20/day20.cpp
262
day20/day20.cpp
|
@ -8,6 +8,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
constexpr auto TILE_ID_PATTERN = R"(Tile (\d+):)";
|
constexpr auto TILE_ID_PATTERN = R"(Tile (\d+):)";
|
||||||
|
@ -15,6 +16,9 @@ constexpr int NUM_CAMERA_LINES = 10;
|
||||||
// Plus one for the ID line, plus one for the newline after
|
// Plus one for the ID line, plus one for the newline after
|
||||||
constexpr int NUM_INPUT_BLOCK_LINES = NUM_CAMERA_LINES + 2;
|
constexpr int NUM_INPUT_BLOCK_LINES = NUM_CAMERA_LINES + 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single frame captured by the camera
|
||||||
|
*/
|
||||||
class CameraFrame {
|
class CameraFrame {
|
||||||
public:
|
public:
|
||||||
CameraFrame(int id, const std::vector<std::string> &frame) : id(id), frame(frame) {
|
CameraFrame(int id, const std::vector<std::string> &frame) : id(id), frame(frame) {
|
||||||
|
@ -24,18 +28,31 @@ class CameraFrame {
|
||||||
return this->id;
|
return this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the contents of this frame
|
||||||
|
* @return const std::vector<std::string>& The frame contents
|
||||||
|
*/
|
||||||
const std::vector<std::string> &getFrame() const {
|
const std::vector<std::string> &getFrame() const {
|
||||||
return this->frame;
|
return this->frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return const std::string The top edge of this frame
|
||||||
|
*/
|
||||||
const std::string getTopEdge() const {
|
const std::string getTopEdge() const {
|
||||||
return this->frame.front();
|
return this->frame.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return const std::string The bottom edge of this frame
|
||||||
|
*/
|
||||||
const std::string getBottomEdge() const {
|
const std::string getBottomEdge() const {
|
||||||
return this->frame.back();
|
return this->frame.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return const std::string The left edge of this frame
|
||||||
|
*/
|
||||||
const std::string getLeftEdge() const {
|
const std::string getLeftEdge() const {
|
||||||
std::string edge;
|
std::string edge;
|
||||||
std::transform(
|
std::transform(
|
||||||
|
@ -47,6 +64,9 @@ class CameraFrame {
|
||||||
return edge;
|
return edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return const std::string The right edge of this frame
|
||||||
|
*/
|
||||||
const std::string getRightEdge() const {
|
const std::string getRightEdge() const {
|
||||||
std::string edge;
|
std::string edge;
|
||||||
std::transform(
|
std::transform(
|
||||||
|
@ -66,16 +86,25 @@ class CameraFrame {
|
||||||
return !(*this == frame);
|
return !(*this == frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flip this frame along the horizontal axis
|
||||||
|
*/
|
||||||
void flipFrameVertically() {
|
void flipFrameVertically() {
|
||||||
std::reverse(this->frame.begin(), this->frame.end());
|
std::reverse(this->frame.begin(), this->frame.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flip this frame along the vertical axis
|
||||||
|
*/
|
||||||
void flipFrameHorizontally() {
|
void flipFrameHorizontally() {
|
||||||
std::for_each(this->frame.begin(), this->frame.end(), [](std::string &frameLine) {
|
std::for_each(this->frame.begin(), this->frame.end(), [](std::string &frameLine) {
|
||||||
std::reverse(frameLine.begin(), frameLine.end());
|
std::reverse(frameLine.begin(), frameLine.end());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate this frame 90 degrees
|
||||||
|
*/
|
||||||
void rotateFrame90Deg() {
|
void rotateFrame90Deg() {
|
||||||
// This algorithm works by rotating each "ring" of the frame 90 degrees
|
// This algorithm works by rotating each "ring" of the frame 90 degrees
|
||||||
// Consider the following 5x5 frame
|
// Consider the following 5x5 frame
|
||||||
|
@ -122,11 +151,17 @@ class CameraFrame {
|
||||||
this->frame = std::move(rotatedFrame);
|
this->frame = std::move(rotatedFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate this frame 180 degrees
|
||||||
|
*/
|
||||||
void rotateFrame180Deg() {
|
void rotateFrame180Deg() {
|
||||||
this->rotateFrame90Deg();
|
this->rotateFrame90Deg();
|
||||||
this->rotateFrame90Deg();
|
this->rotateFrame90Deg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate this frame 270 degrees
|
||||||
|
*/
|
||||||
void rotateFrame270Deg() {
|
void rotateFrame270Deg() {
|
||||||
this->rotateFrame180Deg();
|
this->rotateFrame180Deg();
|
||||||
this->rotateFrame90Deg();
|
this->rotateFrame90Deg();
|
||||||
|
@ -137,6 +172,106 @@ class CameraFrame {
|
||||||
std::vector<std::string> frame;
|
std::vector<std::string> frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates all of the possible transforms for a given camera frame.
|
||||||
|
*/
|
||||||
|
class TransformGenerator {
|
||||||
|
public:
|
||||||
|
class const_iterator {
|
||||||
|
public:
|
||||||
|
using difference_type = int;
|
||||||
|
using value_type = std::function<CameraFrame()>;
|
||||||
|
using pointer = const value_type *;
|
||||||
|
using reference = const value_type &;
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
|
||||||
|
const_iterator &operator++() {
|
||||||
|
++this->baseIterator;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator operator++(int) {
|
||||||
|
const_iterator res = *this;
|
||||||
|
++(*this);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const const_iterator &other) {
|
||||||
|
return this->baseIterator == other.baseIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const const_iterator &other) {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type operator*() {
|
||||||
|
return *(this->baseIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend TransformGenerator;
|
||||||
|
const_iterator(const TransformGenerator &container)
|
||||||
|
: container(container), baseIterator(container.operationList.cbegin()) {
|
||||||
|
}
|
||||||
|
const_iterator(const TransformGenerator &container, const std::vector<value_type>::const_iterator &baseIterator)
|
||||||
|
: container(container), baseIterator(baseIterator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransformGenerator &container;
|
||||||
|
std::vector<value_type>::const_iterator baseIterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
TransformGenerator(const CameraFrame &frame)
|
||||||
|
: frame(frame),
|
||||||
|
operationList{
|
||||||
|
[frame]() { return frame; },
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.rotateFrame90Deg();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.rotateFrame180Deg();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.rotateFrame270Deg();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.flipFrameVertically();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.flipFrameHorizontally();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.flipFrameHorizontally();
|
||||||
|
frame.rotateFrame90Deg();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
// We don't need one for rotating 180 after flipping, as it's equivalent to flipping vertically
|
||||||
|
[frame = frame]() mutable {
|
||||||
|
frame.flipFrameHorizontally();
|
||||||
|
frame.rotateFrame270Deg();
|
||||||
|
return frame;
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const {
|
||||||
|
return const_iterator(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cend() const {
|
||||||
|
return const_iterator(*this, this->operationList.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CameraFrame frame;
|
||||||
|
std::vector<std::function<CameraFrame()>> operationList;
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<std::string> readInput(const std::string &filename) {
|
std::vector<std::string> readInput(const std::string &filename) {
|
||||||
std::vector<std::string> input;
|
std::vector<std::string> input;
|
||||||
std::string line;
|
std::string line;
|
||||||
|
@ -148,6 +283,11 @@ std::vector<std::string> readInput(const std::string &filename) {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the frame ID from an input line containing one
|
||||||
|
* @param line The frame ID line
|
||||||
|
* @return int The frame ID
|
||||||
|
*/
|
||||||
int getFrameIDFromIDLine(const std::string &line) {
|
int getFrameIDFromIDLine(const std::string &line) {
|
||||||
std::regex pattern(TILE_ID_PATTERN);
|
std::regex pattern(TILE_ID_PATTERN);
|
||||||
std::smatch matches;
|
std::smatch matches;
|
||||||
|
@ -158,6 +298,11 @@ int getFrameIDFromIDLine(const std::string &line) {
|
||||||
return std::stoi(matches[1]);
|
return std::stoi(matches[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the puzzle input
|
||||||
|
* @param input The puzzle input
|
||||||
|
* @return std::vector<CameraFrame> The frames from the camera input
|
||||||
|
*/
|
||||||
std::vector<CameraFrame> parseInput(const std::vector<std::string> &input) {
|
std::vector<CameraFrame> parseInput(const std::vector<std::string> &input) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
std::vector<CameraFrame> cameraFrames;
|
std::vector<CameraFrame> cameraFrames;
|
||||||
|
@ -192,6 +337,12 @@ std::ostream &operator<<(std::ostream &os, const CameraFrame &frame) {
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debugging method to print the entire board
|
||||||
|
* @param board The board
|
||||||
|
* @param maxRow The maximum row in the board
|
||||||
|
* @param maxCol The maximum column in the board
|
||||||
|
*/
|
||||||
void printBoard(const std::map<std::pair<int, int>, CameraFrame> &board, int maxRow, int maxCol) {
|
void printBoard(const std::map<std::pair<int, int>, CameraFrame> &board, int maxRow, int maxCol) {
|
||||||
CameraFrame emptyFrame(0, std::vector<std::string>(NUM_CAMERA_LINES, std::string(NUM_CAMERA_LINES, ' ')));
|
CameraFrame emptyFrame(0, std::vector<std::string>(NUM_CAMERA_LINES, std::string(NUM_CAMERA_LINES, ' ')));
|
||||||
for (int i = 0; i < maxRow; i++) {
|
for (int i = 0; i < maxRow; i++) {
|
||||||
|
@ -209,36 +360,14 @@ void printBoard(const std::map<std::pair<int, int>, CameraFrame> &board, int max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CameraFrame> getPossibleTransforms(const CameraFrame &frame) {
|
/**
|
||||||
CameraFrame identity(frame);
|
* Check if the board has all tiles filled
|
||||||
CameraFrame rotated90Deg(frame);
|
* @param board The board
|
||||||
rotated90Deg.rotateFrame90Deg();
|
* @param maxRow The maximum row of the board
|
||||||
CameraFrame rotated180Deg(frame);
|
* @param maxCol The maximum column of the board
|
||||||
rotated180Deg.rotateFrame180Deg();
|
* @return true If the board is filled
|
||||||
CameraFrame rotated270Deg(frame);
|
* @return false If the board is not filled
|
||||||
rotated270Deg.rotateFrame270Deg();
|
*/
|
||||||
CameraFrame flippedVertically(frame);
|
|
||||||
flippedVertically.flipFrameVertically();
|
|
||||||
CameraFrame flippedHorizontally(frame);
|
|
||||||
flippedHorizontally.flipFrameHorizontally();
|
|
||||||
CameraFrame flippedAndRotated90(flippedHorizontally);
|
|
||||||
flippedAndRotated90.rotateFrame90Deg();
|
|
||||||
// We don't need flip and rotate 180 because it is equivalent to flipVertically
|
|
||||||
CameraFrame flippedAndRotated270(flippedHorizontally);
|
|
||||||
flippedAndRotated270.rotateFrame270Deg();
|
|
||||||
std::vector<CameraFrame> operations{
|
|
||||||
identity,
|
|
||||||
rotated90Deg,
|
|
||||||
rotated180Deg,
|
|
||||||
rotated270Deg,
|
|
||||||
flippedVertically,
|
|
||||||
flippedHorizontally,
|
|
||||||
flippedAndRotated90,
|
|
||||||
flippedAndRotated270};
|
|
||||||
|
|
||||||
return operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isBoardFilled(const std::map<std::pair<int, int>, CameraFrame> board, int maxRow, int maxCol) {
|
bool isBoardFilled(const std::map<std::pair<int, int>, CameraFrame> board, int maxRow, int maxCol) {
|
||||||
for (int i = 0; i < maxRow; i++) {
|
for (int i = 0; i < maxRow; i++) {
|
||||||
for (int j = 0; j < maxCol; j++) {
|
for (int j = 0; j < maxCol; j++) {
|
||||||
|
@ -251,16 +380,34 @@ bool isBoardFilled(const std::map<std::pair<int, int>, CameraFrame> board, int m
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a possible matching frame for this frame
|
||||||
|
* @param frameToMatch The frame to check
|
||||||
|
* @param frameMatches A function to check if a given frame matches this one
|
||||||
|
* @return std::optional<CameraFrame> The matching frame, if ti exists
|
||||||
|
*/
|
||||||
std::optional<CameraFrame> findPossibleFrame(
|
std::optional<CameraFrame> findPossibleFrame(
|
||||||
const std::vector<CameraFrame> &availableFrames, std::function<bool(const CameraFrame &)> findFrame) {
|
const CameraFrame &frameToMatch, std::function<bool(const CameraFrame &)> frameMatches) {
|
||||||
auto result = std::find_if(availableFrames.cbegin(), availableFrames.cend(), findFrame);
|
auto transformations = TransformGenerator(frameToMatch);
|
||||||
if (result == availableFrames.cend()) {
|
auto result = std::find_if(transformations.cbegin(), transformations.cend(), [frameMatches](auto transformation) {
|
||||||
|
CameraFrame transformedFrame = transformation();
|
||||||
|
return frameMatches(transformedFrame);
|
||||||
|
});
|
||||||
|
if (result == transformations.cend()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *result;
|
return (*result)();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a board that correctly lines everything up
|
||||||
|
* @param startingFrame The frame to start with
|
||||||
|
* @param frames All the other frames available for use
|
||||||
|
* @param maxRow The maximum row size
|
||||||
|
* @param maxCol The maximum column size
|
||||||
|
* @return std::optional<std::map<std::pair<int, int>, CameraFrame>> The board that solves part 1
|
||||||
|
*/
|
||||||
std::optional<std::map<std::pair<int, int>, CameraFrame>> findLinedUpArrangement(
|
std::optional<std::map<std::pair<int, int>, CameraFrame>> findLinedUpArrangement(
|
||||||
const CameraFrame &startingFrame, const std::vector<CameraFrame> &frames, int maxRow, int maxCol) {
|
const CameraFrame &startingFrame, const std::vector<CameraFrame> &frames, int maxRow, int maxCol) {
|
||||||
std::map<std::pair<int, int>, CameraFrame> board;
|
std::map<std::pair<int, int>, CameraFrame> board;
|
||||||
|
@ -274,31 +421,24 @@ std::optional<std::map<std::pair<int, int>, CameraFrame>> findLinedUpArrangement
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (const CameraFrame ¤tFrame : availableFrames) {
|
for (const CameraFrame ¤tFrame : availableFrames) {
|
||||||
auto transformed = getPossibleTransforms(currentFrame);
|
std::optional<CameraFrame> matchingFrame;
|
||||||
for (const CameraFrame ¤tTransformedFrame : transformed) {
|
if (col == 0) {
|
||||||
std::optional<CameraFrame> matchingFrame;
|
CameraFrame &aboveFrame = board.at(std::make_pair(row - 1, col));
|
||||||
if (col == 0) {
|
matchingFrame = findPossibleFrame(currentFrame, [aboveFrame](const CameraFrame &frame) {
|
||||||
CameraFrame &aboveFrame = board.at(std::make_pair(row - 1, col));
|
return aboveFrame.getBottomEdge() == frame.getTopEdge();
|
||||||
matchingFrame = findPossibleFrame(transformed, [aboveFrame](const CameraFrame &frame) {
|
});
|
||||||
return aboveFrame.getBottomEdge() == frame.getTopEdge();
|
} else {
|
||||||
});
|
CameraFrame &leftFrame = board.at(std::make_pair(row, col - 1));
|
||||||
} else {
|
matchingFrame = findPossibleFrame(currentFrame, [leftFrame](const CameraFrame &frame) {
|
||||||
CameraFrame &leftFrame = board.at(std::make_pair(row, col - 1));
|
return leftFrame.getRightEdge() == frame.getLeftEdge();
|
||||||
matchingFrame = findPossibleFrame(transformed, [leftFrame](const CameraFrame &frame) {
|
});
|
||||||
return leftFrame.getRightEdge() == frame.getLeftEdge();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchingFrame) {
|
|
||||||
found = true;
|
|
||||||
board.emplace(std::make_pair(row, col), *matchingFrame);
|
|
||||||
availableFrames.erase(
|
|
||||||
std::remove(availableFrames.begin(), availableFrames.end(), currentFrame));
|
|
||||||
// printBoard(board, maxRow, maxCol);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (found) {
|
|
||||||
|
if (matchingFrame) {
|
||||||
|
found = true;
|
||||||
|
board.emplace(std::make_pair(row, col), *matchingFrame);
|
||||||
|
availableFrames.erase(std::remove(availableFrames.begin(), availableFrames.end(), currentFrame));
|
||||||
|
// printBoard(board, maxRow, maxCol);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,8 +461,10 @@ long part1(const std::vector<CameraFrame> &frames) {
|
||||||
std::vector<CameraFrame> availableFrames(frames);
|
std::vector<CameraFrame> availableFrames(frames);
|
||||||
availableFrames.erase(std::remove(availableFrames.begin(), availableFrames.end(), frame));
|
availableFrames.erase(std::remove(availableFrames.begin(), availableFrames.end(), frame));
|
||||||
|
|
||||||
auto transformed = getPossibleTransforms(frame);
|
const TransformGenerator transforms(frame);
|
||||||
for (const CameraFrame &transformedFrame : transformed) {
|
for (auto it = transforms.cbegin(); it != transforms.cend(); ++it) {
|
||||||
|
auto transformation = *it;
|
||||||
|
CameraFrame transformedFrame = transformation();
|
||||||
auto res = findLinedUpArrangement(transformedFrame, availableFrames, boardSize, boardSize);
|
auto res = findLinedUpArrangement(transformedFrame, availableFrames, boardSize, boardSize);
|
||||||
if (res) {
|
if (res) {
|
||||||
return 1L * res->at(std::make_pair(0, 0)).getID() * res->at(std::make_pair(0, boardSize - 1)).getID() *
|
return 1L * res->at(std::make_pair(0, 0)).getID() * res->at(std::make_pair(0, boardSize - 1)).getID() *
|
||||||
|
|
Loading…
Reference in New Issue