diff --git a/day7/Makefile b/day7/Makefile new file mode 100644 index 0000000..9acfc18 --- /dev/null +++ b/day7/Makefile @@ -0,0 +1,15 @@ +CC=g++ +BIN_NAME=day7 +CCFLAGS=-o $(BIN_NAME) -g -std=c++17 +LDFLAGS=-lfolly + +.PHONY: all, clean + +all: $(BIN_NAME) + +clean: + rm -f $(BIN_NAME) + +$(BIN_NAME): day7.cpp + $(CC) $(CCFLAGS) $(LDFLAGS) day7.cpp + diff --git a/day7/day7.cpp b/day7/day7.cpp new file mode 100644 index 0000000..a04e2a0 --- /dev/null +++ b/day7/day7.cpp @@ -0,0 +1,144 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +auto constexpr LINE_PATTERN = R"((.*) bags? contain (.*)\.)"; +auto constexpr BAG_PATTERN = R"((\d) (.*) bags?)"; +auto constexpr NO_OTHER_BAGS = "no other bags"; +auto constexpr BAG_DELIM = ", "; +auto constexpr DESIRED_BAG = "shiny gold"; + +class ContainedBag { + public: + ContainedBag(std::string color, int quantity) : color(color), quantity(quantity) { + } + + const std::string &getColor() const { + return this->color; + } + + int getQuantity() const { + return this->quantity; + } + + bool operator==(ContainedBag bag) { + return bag.getColor() == this->getColor() && bag.getQuantity() == this->getQuantity(); + } + + private: + std::string color; + int quantity; +}; + +// I would use a set for the value but I don't want to deal with hash functions right now +using BagMap = std::map>; + +std::vector readInput(const std::string &filename) { + std::vector input; + std::string line; + std::ifstream file(filename); + while (std::getline(file, line)) { + input.push_back(line); + } + + return input; +} + +std::pair> parseInputLine(const std::string &line) { + std::regex lineExpression(LINE_PATTERN); + std::smatch lineMatches; + if (!std::regex_match(line, lineMatches, lineExpression)) { + throw new std::invalid_argument("Invalid input line"); + } + + std::string bagName = lineMatches[1]; + std::string allUnparsedBags = lineMatches[2]; + std::vector unparsedBags; + folly::split(BAG_DELIM, allUnparsedBags, unparsedBags); + std::vector containedBags; + for (const std::string &unparsedBag : unparsedBags) { + // Check for no bags contained + if (unparsedBag == NO_OTHER_BAGS) { + continue; + } + + // Check for some quantity of bags contained + std::regex bagExpression(BAG_PATTERN); + std::smatch bagMatches; + if (!std::regex_match(unparsedBag, bagMatches, bagExpression)) { + throw new std::invalid_argument("Invalid bagspec"); + } + + ContainedBag containedBag(bagMatches[2], std::stoi(bagMatches[1])); + containedBags.push_back(containedBag); + } + + return std::pair>(bagName, containedBags); +} + +BagMap makeBagMap(const std::vector &input) { + std::map> bags; + std::transform(input.cbegin(), input.cend(), std::inserter(bags, bags.begin()), [](const std::string &line) { + return parseInputLine(line); + }); + + return bags; +} + +bool doesBagContain(BagMap bagMap, const std::string &originBag, const std::string &desiredBag) { + std::stack toVisit; + std::set visited; + toVisit.emplace(originBag); + while (!toVisit.empty()) { + std::string visiting = toVisit.top(); + toVisit.pop(); + if (visited.find(visiting) != visited.end()) { + continue; + } + + if (visiting == desiredBag) { + return true; + } + + for (ContainedBag bag : bagMap.at(visiting)) { + toVisit.emplace(std::string(bag.getColor())); + } + } + + return false; +} + +int part1(const std::vector &input) { + auto bagMap = makeBagMap(input); + int count = 0; + for (auto bagEntry : bagMap) { + const std::string &bagName = bagEntry.first; + if (bagName == DESIRED_BAG) { + continue; + } + + count += doesBagContain(bagMap, bagName, DESIRED_BAG); + } + + return count; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + std::cerr << argv[0] << " " << std::endl; + return 1; + } + + auto input = readInput(argv[1]); + std::cout << part1(input) << std::endl; + // std::cout << part2(groups) << std::endl; +}