From ae8847bdd11f4c3b116dce0b0e4052a1d4a44d40 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Sat, 19 Dec 2020 02:02:53 -0500 Subject: [PATCH] Add solution to day 19 part 1 --- day19/Makefile | 15 ++++ day19/day19.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 day19/Makefile create mode 100644 day19/day19.cpp diff --git a/day19/Makefile b/day19/Makefile new file mode 100644 index 0000000..eaf7c14 --- /dev/null +++ b/day19/Makefile @@ -0,0 +1,15 @@ +CC=g++ +BIN_NAME=day19 +CCFLAGS=-o $(BIN_NAME) -g -std=c++17 +LDFLAGS=-lfolly + +.PHONY: all, clean + +all: $(BIN_NAME) + +clean: + rm -f $(BIN_NAME) + +$(BIN_NAME): day19.cpp + $(CC) $(CCFLAGS) $(LDFLAGS) day19.cpp + diff --git a/day19/day19.cpp b/day19/day19.cpp new file mode 100644 index 0000000..587e099 --- /dev/null +++ b/day19/day19.cpp @@ -0,0 +1,178 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr auto ALTERNATING_DELIM = " | "; +class GrammarEntry; +using MultiGrammarEntry = std::vector; +using AlternatableMultiGrammarEntry = std::vector; + +class GrammarEntry { + public: + GrammarEntry(int index) : index(index), isThisALookup(true) { + } + + GrammarEntry(char value) : value(value), isThisALookup(false) { + } + + bool isLookup() const { + return this->isThisALookup; + } + + int getIndex() const { + if (!isThisALookup) { + throw "Cannot get the index of a non-lookup"; + } + + return this->index; + } + + char getValue() const { + if (isThisALookup) { + throw "Cannot get the value of a lookup"; + } + + return this->value; + } + + private: + int index; + char value; + bool isThisALookup; +}; + +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, std::vector> splitInput(const std::vector &input) { + auto emptyLine = std::find(input.cbegin(), input.cend(), ""); + auto beforeEmptyLine = std::vector(input.cbegin(), emptyLine); + auto afterEmptyLine = std::vector(emptyLine + 1, input.cend()); + return std::pair, std::vector>( + std::move(beforeEmptyLine), std::move(afterEmptyLine)); +} + +MultiGrammarEntry parseSinglePattern(const std::string &rawPattern) { + std::vector rawPatternComponents; + folly::split(" ", rawPattern, rawPatternComponents); + MultiGrammarEntry patternComponents; + std::transform( + rawPatternComponents.cbegin(), + rawPatternComponents.cend(), + std::back_inserter(patternComponents), + [](const std::string &rawComponent) { + if (rawComponent.at(0) == '"' && rawComponent.at(rawComponent.size() - 1) == '"') { + std::string component = rawComponent.substr(1, rawComponent.size() - 2); + if (component.size() != 1) { + throw std::invalid_argument("Base rule must be 1 char"); + } + + return GrammarEntry(component.at(0)); + } else { + return GrammarEntry(std::stoi(rawComponent)); + } + }); + + return patternComponents; +} + +std::unordered_map parseGrammar(const std::vector &patterns) { + std::unordered_map grammar; + std::transform( + patterns.cbegin(), patterns.cend(), std::inserter(grammar, grammar.end()), [](const std::string &patternLine) { + auto colonIndex = patternLine.find(":"); + std::string rawIndex = patternLine.substr(0, colonIndex); + int patternIndex = std::stoi(rawIndex); + std::vector rawAlternations; + // An extra +1 on the colon index to get rid of the space after the colon + folly::split(ALTERNATING_DELIM, patternLine.substr(colonIndex + 2), rawAlternations); + + AlternatableMultiGrammarEntry entries; + std::transform( + rawAlternations.cbegin(), rawAlternations.cend(), std::back_inserter(entries), parseSinglePattern); + + return std::pair(patternIndex, entries); + }); + + return grammar; +} + +int matchesHowManyChars( + const std::unordered_map &grammar, const std::string_view toParse, int rule = 0, + int depth = 0) { + auto indentation = std::string(depth, '|'); + std::cout << indentation << "Working on " << toParse << std::endl; + AlternatableMultiGrammarEntry ruleAlternations = grammar.at(rule); + std::optional> best; + for (const MultiGrammarEntry &alternation : ruleAlternations) { + int numMatched = 0; + int i = 0; + for (auto entryIt = alternation.cbegin(); entryIt != alternation.cend(); (++entryIt, i++)) { + GrammarEntry entry = *entryIt; + int ruleMatchCount; + if (entry.isLookup()) { + std::cout << indentation << "Checking rule " << entry.getIndex() << std::endl; + ruleMatchCount = matchesHowManyChars(grammar, toParse.substr(numMatched), entry.getIndex(), depth + 1); + } else { + std::cout << indentation << "Checking rule " << entry.getValue() << std::endl; + char toMatch = toParse.at(i); + char ruleValue = entry.getValue(); + ruleMatchCount = (toMatch == ruleValue); + } + + std::cout << indentation << "Result: " << numMatched << std::endl; + + if (!ruleMatchCount) { + break; + } + + numMatched += ruleMatchCount; + } + + std::cout << indentation << "Full result: " << numMatched << std::endl; + if (!best || numMatched > best->second) { + best = std::pair(alternation, numMatched); + } + } + + if (!best) { + return 0; + } + + return best->second; +} + +int part1(const std::vector &patterns, const std::vector &testStrings) { + std::unordered_map grammar = parseGrammar(patterns); + return std::count_if(testStrings.cbegin(), testStrings.cend(), [&grammar](const std::string &testString) { + return matchesHowManyChars(grammar, testString) == testString.length(); + }); +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + std::cerr << argv[0] << " " << std::endl; + return 1; + } + + auto input = readInput(argv[1]); + auto parsedInput = splitInput(input); + + std::cout << part1(parsedInput.first, parsedInput.second) << std::endl; +}