Add day 18 part 2
parent
635cd054b9
commit
3d9a06db27
136
day18/day18.cpp
136
day18/day18.cpp
|
@ -12,11 +12,10 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
enum Operation { ADDITION = '+', MULTIPLICATION = '*' };
|
enum Operation { ADDITION = '+', MULTIPLICATION = '*', IDENTITY = 'i' };
|
||||||
class ExpressionNode;
|
class ExpressionNode;
|
||||||
using EvaluationStrategy = std::function<double(
|
using EvaluationStrategy =
|
||||||
const ExpressionNode &, const std::unique_ptr<ExpressionNode> &, const std::unique_ptr<ExpressionNode> &,
|
std::function<double(const std::unique_ptr<ExpressionNode> &, const std::unique_ptr<ExpressionNode> &, Operation)>;
|
||||||
Operation)>;
|
|
||||||
|
|
||||||
class ExpressionNode {
|
class ExpressionNode {
|
||||||
public:
|
public:
|
||||||
|
@ -24,7 +23,8 @@ class ExpressionNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual long evaluate() const = 0;
|
virtual long evaluate() const = 0;
|
||||||
virtual int numChildren() const = 0;
|
virtual std::vector<std::reference_wrapper<const ExpressionNode>> getChildren() const = 0;
|
||||||
|
virtual Operation getOperation() const = 0;
|
||||||
|
|
||||||
virtual ~ExpressionNode() {
|
virtual ~ExpressionNode() {
|
||||||
}
|
}
|
||||||
|
@ -35,12 +35,16 @@ class ValueNode : public ExpressionNode {
|
||||||
ValueNode(long n) : n(n) {
|
ValueNode(long n) : n(n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
long evaluate() const {
|
long evaluate() const override {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
int numChildren() const {
|
std::vector<std::reference_wrapper<const ExpressionNode>> getChildren() const override {
|
||||||
return 1;
|
return std::vector<std::reference_wrapper<const ExpressionNode>>{*this};
|
||||||
|
}
|
||||||
|
|
||||||
|
Operation getOperation() const {
|
||||||
|
return IDENTITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -49,7 +53,7 @@ class ValueNode : public ExpressionNode {
|
||||||
|
|
||||||
class ExpressionTree : public ExpressionNode {
|
class ExpressionTree : public ExpressionNode {
|
||||||
public:
|
public:
|
||||||
ExpressionTree(EvaluationStrategy strategy) : strategy(strategy) {
|
ExpressionTree(EvaluationStrategy strategy) : strategy(strategy), isParenthesized(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLeft(std::unique_ptr<ExpressionNode> &&left) {
|
void setLeft(std::unique_ptr<ExpressionNode> &&left) {
|
||||||
|
@ -60,8 +64,8 @@ class ExpressionTree : public ExpressionNode {
|
||||||
this->right = std::move(left);
|
this->right = std::move(left);
|
||||||
}
|
}
|
||||||
|
|
||||||
int numChildren() const {
|
void setIsParenthesized(bool isParenthesized) {
|
||||||
return 2;
|
this->isParenthesized = isParenthesized;
|
||||||
}
|
}
|
||||||
|
|
||||||
void putNextNode(std::unique_ptr<ExpressionNode> &&node) {
|
void putNextNode(std::unique_ptr<ExpressionNode> &&node) {
|
||||||
|
@ -78,7 +82,7 @@ class ExpressionTree : public ExpressionNode {
|
||||||
this->op = op;
|
this->op = op;
|
||||||
}
|
}
|
||||||
|
|
||||||
long evaluate() const {
|
long evaluate() const override {
|
||||||
if (!this->canFullyEvaluate()) {
|
if (!this->canFullyEvaluate()) {
|
||||||
if (this->left) {
|
if (this->left) {
|
||||||
return this->left->evaluate();
|
return this->left->evaluate();
|
||||||
|
@ -89,16 +93,43 @@ class ExpressionTree : public ExpressionNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->strategy) {
|
return this->strategy(this->left, this->right, *(this->op));
|
||||||
throw "AAA";
|
}
|
||||||
|
|
||||||
|
std::vector<std::reference_wrapper<const ExpressionNode>> getChildren() const override {
|
||||||
|
if (!this->left && !this->right) {
|
||||||
|
throw "Cannot evaluate empty tree's children";
|
||||||
|
} else if (this->left && this->right) {
|
||||||
|
return std::vector<std::reference_wrapper<const ExpressionNode>>{
|
||||||
|
*this->left,
|
||||||
|
*this->right,
|
||||||
|
};
|
||||||
|
} else if (this->left) {
|
||||||
|
return std::vector<std::reference_wrapper<const ExpressionNode>>{
|
||||||
|
*this->left,
|
||||||
|
};
|
||||||
|
} else if (this->right) {
|
||||||
|
return std::vector<std::reference_wrapper<const ExpressionNode>>{
|
||||||
|
*this->right,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw "Invalid state";
|
||||||
}
|
}
|
||||||
return this->strategy(*this, this->left, this->right, *(this->op));
|
}
|
||||||
|
|
||||||
|
Operation getOperation() const {
|
||||||
|
if (!this->op) {
|
||||||
|
throw "Cannot get an unset operator";
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this->op;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ExpressionNode> left;
|
std::unique_ptr<ExpressionNode> left;
|
||||||
std::unique_ptr<ExpressionNode> right;
|
std::unique_ptr<ExpressionNode> right;
|
||||||
std::optional<Operation> op;
|
std::optional<Operation> op;
|
||||||
|
bool isParenthesized;
|
||||||
EvaluationStrategy strategy;
|
EvaluationStrategy strategy;
|
||||||
|
|
||||||
bool canFullyEvaluate() const {
|
bool canFullyEvaluate() const {
|
||||||
|
@ -134,8 +165,8 @@ std::optional<Operation> parseOperator(const std::string_view component) {
|
||||||
|
|
||||||
std::optional<long> parseNumber(const std::string_view component) {
|
std::optional<long> parseNumber(const std::string_view component) {
|
||||||
try {
|
try {
|
||||||
// This will only really ever copy some (usually small) number of digits... I don't consider it a very expensive
|
// This will only really ever copy some (usually small) number of digits... I don't consider it a very
|
||||||
// copy
|
// expensive copy
|
||||||
return std::stol(std::string(component));
|
return std::stol(std::string(component));
|
||||||
} catch (std::invalid_argument) {
|
} catch (std::invalid_argument) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -167,7 +198,7 @@ std::optional<std::pair<std::string_view, int>> parseParenthetical(int cursor, c
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ExpressionNode> buildTree(
|
std::unique_ptr<ExpressionNode> buildTree(
|
||||||
const std::string_view input, const EvaluationStrategy &strategy, int depth = 0) {
|
const std::string_view input, const EvaluationStrategy &strategy, bool inParenthetical = false, int depth = 0) {
|
||||||
if (std::count_if(input.cbegin(), input.cend(), [](char c) { return c == ' '; }) == 0) {
|
if (std::count_if(input.cbegin(), input.cend(), [](char c) { return c == ' '; }) == 0) {
|
||||||
std::cout << std::string(depth, ' ') << "RIGHT: " << input << std::endl;
|
std::cout << std::string(depth, ' ') << "RIGHT: " << input << std::endl;
|
||||||
std::optional<long> value = parseNumber(input);
|
std::optional<long> value = parseNumber(input);
|
||||||
|
@ -178,10 +209,8 @@ std::unique_ptr<ExpressionNode> buildTree(
|
||||||
return std::make_unique<ValueNode>(*value);
|
return std::make_unique<ValueNode>(*value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strategy) {
|
|
||||||
throw "AAAA";
|
|
||||||
}
|
|
||||||
ExpressionTree tree(strategy);
|
ExpressionTree tree(strategy);
|
||||||
|
tree.setIsParenthesized(inParenthetical);
|
||||||
// This should technically be size_type but I need to be able to go before zero
|
// This should technically be size_type but I need to be able to go before zero
|
||||||
int cursor = input.size() - 1;
|
int cursor = input.size() - 1;
|
||||||
while (cursor > 0) {
|
while (cursor > 0) {
|
||||||
|
@ -196,7 +225,7 @@ std::unique_ptr<ExpressionNode> buildTree(
|
||||||
parseParenthetical(previousSpace - 1, input);
|
parseParenthetical(previousSpace - 1, input);
|
||||||
if (parentheticalPart) {
|
if (parentheticalPart) {
|
||||||
std::cout << std::string(depth, ' ') << "RIGHT (paren): " << parentheticalPart->first << std::endl;
|
std::cout << std::string(depth, ' ') << "RIGHT (paren): " << parentheticalPart->first << std::endl;
|
||||||
auto parentheticalTree = buildTree(parentheticalPart->first, strategy, depth + 1);
|
auto parentheticalTree = buildTree(parentheticalPart->first, strategy, true, depth + 1);
|
||||||
tree.setRight(std::move(parentheticalTree));
|
tree.setRight(std::move(parentheticalTree));
|
||||||
cursor = parentheticalPart->second - 2;
|
cursor = parentheticalPart->second - 2;
|
||||||
continue;
|
continue;
|
||||||
|
@ -208,7 +237,7 @@ std::unique_ptr<ExpressionNode> buildTree(
|
||||||
tree.setOp(*componentOperation);
|
tree.setOp(*componentOperation);
|
||||||
auto rest = input.substr(0, cursor + 1);
|
auto rest = input.substr(0, cursor + 1);
|
||||||
std::cout << std::string(depth, ' ') << "LEFT: " << component << std::endl;
|
std::cout << std::string(depth, ' ') << "LEFT: " << component << std::endl;
|
||||||
auto rightTree = buildTree(rest, strategy, depth + 1);
|
auto rightTree = buildTree(rest, strategy, false, depth + 1);
|
||||||
tree.setLeft(std::move(rightTree));
|
tree.setLeft(std::move(rightTree));
|
||||||
// Once we have found an operator and the operand to the left of it, we're done
|
// Once we have found an operator and the operand to the left of it, we're done
|
||||||
break;
|
break;
|
||||||
|
@ -234,8 +263,7 @@ long run(const std::vector<std::string> &input, const EvaluationStrategy &strate
|
||||||
}
|
}
|
||||||
|
|
||||||
long part1(const std::vector<std::string> &input) {
|
long part1(const std::vector<std::string> &input) {
|
||||||
EvaluationStrategy strategy = [](const ExpressionNode &self,
|
EvaluationStrategy strategy = [](const std::unique_ptr<ExpressionNode> &left,
|
||||||
const std::unique_ptr<ExpressionNode> &left,
|
|
||||||
const std::unique_ptr<ExpressionNode> &right,
|
const std::unique_ptr<ExpressionNode> &right,
|
||||||
Operation op) -> long {
|
Operation op) -> long {
|
||||||
long leftValue = left->evaluate();
|
long leftValue = left->evaluate();
|
||||||
|
@ -253,6 +281,61 @@ long part1(const std::vector<std::string> &input) {
|
||||||
|
|
||||||
return run(input, strategy);
|
return run(input, strategy);
|
||||||
}
|
}
|
||||||
|
long part2(const std::vector<std::string> &input) {
|
||||||
|
EvaluationStrategy strategy = [](const std::unique_ptr<ExpressionNode> &left,
|
||||||
|
const std::unique_ptr<ExpressionNode> &right,
|
||||||
|
Operation op) -> long {
|
||||||
|
auto leftChildren = left->getChildren();
|
||||||
|
if (leftChildren.size() == 1 && op == ADDITION) {
|
||||||
|
return leftChildren.at(0).get().evaluate() + right->evaluate();
|
||||||
|
} else if (leftChildren.size() == 1 && op == MULTIPLICATION) {
|
||||||
|
return leftChildren.at(0).get().evaluate() * right->evaluate();
|
||||||
|
} else if (leftChildren.size() != 2) {
|
||||||
|
throw std::invalid_argument("Cannot perform unknown binary operation on children");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == MULTIPLICATION) {
|
||||||
|
return right->evaluate() * left->evaluate();
|
||||||
|
} else if (op != ADDITION && left->getOperation() != MULTIPLICATION) {
|
||||||
|
throw std::invalid_argument("Cannot perform an unknown binary operation on children");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionNode const *prevCursor = nullptr;
|
||||||
|
ExpressionNode const *cursor = left.get();
|
||||||
|
long total = right->evaluate();
|
||||||
|
while (cursor->getChildren().size() == 2 && cursor->getOperation() == ADDITION) {
|
||||||
|
auto cursorChildren = cursor->getChildren();
|
||||||
|
const ExpressionNode &leftCursorChild = cursorChildren.at(0);
|
||||||
|
const ExpressionNode &rightCursorChild = cursorChildren.at(1);
|
||||||
|
total += rightCursorChild.evaluate();
|
||||||
|
prevCursor = cursor;
|
||||||
|
cursor = &leftCursorChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor->getChildren().size() == 1) {
|
||||||
|
const ExpressionNode &cursorChild = cursor->getChildren().at(0);
|
||||||
|
Operation finalOperation = op;
|
||||||
|
if (prevCursor) {
|
||||||
|
finalOperation = prevCursor->getOperation();
|
||||||
|
}
|
||||||
|
if (finalOperation == ADDITION) {
|
||||||
|
return total + cursorChild.evaluate();
|
||||||
|
} else if (finalOperation == MULTIPLICATION) {
|
||||||
|
return total * cursorChild.evaluate();
|
||||||
|
} else {
|
||||||
|
throw std::invalid_argument("Cannot perform an unknown binary operation on total");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cursorChildren = cursor->getChildren();
|
||||||
|
const ExpressionNode &leftCursorChild = cursorChildren.at(0);
|
||||||
|
const ExpressionNode &rightCursorChild = cursorChildren.at(1);
|
||||||
|
|
||||||
|
return (total + rightCursorChild.evaluate()) * leftCursorChild.evaluate();
|
||||||
|
};
|
||||||
|
|
||||||
|
return run(input, strategy);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
|
@ -262,5 +345,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
auto input = readInput(argv[1]);
|
auto input = readInput(argv[1]);
|
||||||
|
|
||||||
std::cout << part1(input) << std::endl;
|
// std::cout << part1(input) << std::endl;
|
||||||
|
std::cout << part2(input) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue