From 3d9a06db27d2d1b04379b9bb6ddf8fbffcf3c199 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Fri, 18 Dec 2020 21:06:47 -0500 Subject: [PATCH] Add day 18 part 2 --- day18/day18.cpp | 136 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 26 deletions(-) diff --git a/day18/day18.cpp b/day18/day18.cpp index c7ad6cb..0991df5 100644 --- a/day18/day18.cpp +++ b/day18/day18.cpp @@ -12,11 +12,10 @@ #include #include -enum Operation { ADDITION = '+', MULTIPLICATION = '*' }; +enum Operation { ADDITION = '+', MULTIPLICATION = '*', IDENTITY = 'i' }; class ExpressionNode; -using EvaluationStrategy = std::function &, const std::unique_ptr &, - Operation)>; +using EvaluationStrategy = + std::function &, const std::unique_ptr &, Operation)>; class ExpressionNode { public: @@ -24,7 +23,8 @@ class ExpressionNode { } virtual long evaluate() const = 0; - virtual int numChildren() const = 0; + virtual std::vector> getChildren() const = 0; + virtual Operation getOperation() const = 0; virtual ~ExpressionNode() { } @@ -35,12 +35,16 @@ class ValueNode : public ExpressionNode { ValueNode(long n) : n(n) { } - long evaluate() const { + long evaluate() const override { return n; } - int numChildren() const { - return 1; + std::vector> getChildren() const override { + return std::vector>{*this}; + } + + Operation getOperation() const { + return IDENTITY; } private: @@ -49,7 +53,7 @@ class ValueNode : public ExpressionNode { class ExpressionTree : public ExpressionNode { public: - ExpressionTree(EvaluationStrategy strategy) : strategy(strategy) { + ExpressionTree(EvaluationStrategy strategy) : strategy(strategy), isParenthesized(false) { } void setLeft(std::unique_ptr &&left) { @@ -60,8 +64,8 @@ class ExpressionTree : public ExpressionNode { this->right = std::move(left); } - int numChildren() const { - return 2; + void setIsParenthesized(bool isParenthesized) { + this->isParenthesized = isParenthesized; } void putNextNode(std::unique_ptr &&node) { @@ -78,7 +82,7 @@ class ExpressionTree : public ExpressionNode { this->op = op; } - long evaluate() const { + long evaluate() const override { if (!this->canFullyEvaluate()) { if (this->left) { return this->left->evaluate(); @@ -89,16 +93,43 @@ class ExpressionTree : public ExpressionNode { } } - if (!this->strategy) { - throw "AAA"; + return this->strategy(this->left, this->right, *(this->op)); + } + + std::vector> getChildren() const override { + if (!this->left && !this->right) { + throw "Cannot evaluate empty tree's children"; + } else if (this->left && this->right) { + return std::vector>{ + *this->left, + *this->right, + }; + } else if (this->left) { + return std::vector>{ + *this->left, + }; + } else if (this->right) { + return std::vector>{ + *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: std::unique_ptr left; std::unique_ptr right; std::optional op; + bool isParenthesized; EvaluationStrategy strategy; bool canFullyEvaluate() const { @@ -134,8 +165,8 @@ std::optional parseOperator(const std::string_view component) { std::optional parseNumber(const std::string_view component) { try { - // This will only really ever copy some (usually small) number of digits... I don't consider it a very expensive - // copy + // This will only really ever copy some (usually small) number of digits... I don't consider it a very + // expensive copy return std::stol(std::string(component)); } catch (std::invalid_argument) { return std::nullopt; @@ -167,7 +198,7 @@ std::optional> parseParenthetical(int cursor, c } std::unique_ptr 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) { std::cout << std::string(depth, ' ') << "RIGHT: " << input << std::endl; std::optional value = parseNumber(input); @@ -178,10 +209,8 @@ std::unique_ptr buildTree( return std::make_unique(*value); } - if (!strategy) { - throw "AAAA"; - } ExpressionTree tree(strategy); + tree.setIsParenthesized(inParenthetical); // This should technically be size_type but I need to be able to go before zero int cursor = input.size() - 1; while (cursor > 0) { @@ -196,7 +225,7 @@ std::unique_ptr buildTree( parseParenthetical(previousSpace - 1, input); if (parentheticalPart) { 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)); cursor = parentheticalPart->second - 2; continue; @@ -208,7 +237,7 @@ std::unique_ptr buildTree( tree.setOp(*componentOperation); auto rest = input.substr(0, cursor + 1); 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)); // Once we have found an operator and the operand to the left of it, we're done break; @@ -234,8 +263,7 @@ long run(const std::vector &input, const EvaluationStrategy &strate } long part1(const std::vector &input) { - EvaluationStrategy strategy = [](const ExpressionNode &self, - const std::unique_ptr &left, + EvaluationStrategy strategy = [](const std::unique_ptr &left, const std::unique_ptr &right, Operation op) -> long { long leftValue = left->evaluate(); @@ -253,6 +281,61 @@ long part1(const std::vector &input) { return run(input, strategy); } +long part2(const std::vector &input) { + EvaluationStrategy strategy = [](const std::unique_ptr &left, + const std::unique_ptr &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[]) { if (argc != 2) { @@ -262,5 +345,6 @@ int main(int argc, char *argv[]) { auto input = readInput(argv[1]); - std::cout << part1(input) << std::endl; + // std::cout << part1(input) << std::endl; + std::cout << part2(input) << std::endl; }