diff --git a/day19/day19.cpp b/day19/day19.cpp index 2199307..e62db19 100644 --- a/day19/day19.cpp +++ b/day19/day19.cpp @@ -14,10 +14,16 @@ constexpr auto RULE_DELIM = ":"; constexpr auto ALTERNATING_DELIM = " | "; +constexpr int NUM_RULE_11_CYCLES = 8; + class GrammarEntry; using MultiGrammarEntry = std::vector; using AlternatableMultiGrammarEntry = std::vector; +/** + * An entry in the grammar. This can either be a layer of indirection to another entry (a lookup) or a grammar rule + * itself. + */ class GrammarEntry { public: GrammarEntry(int index) : index(index), isThisALookup(true) { @@ -26,18 +32,31 @@ class GrammarEntry { GrammarEntry(char value) : value(value), isThisALookup(false) { } + /** + * Checks whether or not this grammar rule is an indirection to another + * + * @return bool Whether or not thisg rammar ule is an indirection to another. + */ bool isLookup() const { return this->isThisALookup; } + /** + * @return int The index of the rule that this looks up + */ int getIndex() const { - if (!isThisALookup) { + if (!this->isLookup()) { throw "Cannot get the index of a non-lookup"; } return this->index; } + /** + * Checks the value of this grammar rule, if it is not a lookup. + * + * @return The character that this grammar rule represents. + */ char getValue() const { if (isThisALookup) { throw "Cannot get the value of a lookup"; @@ -63,6 +82,14 @@ std::vector readInput(const std::string &filename) { return input; } +/** + * Split the input into grammar and test strings + * + * @param input The puzzle input. + * + * @return std::pair, std::vector> A pair of the puzzle grammar and the puzzle + * test strings + */ std::pair, std::vector> splitInput(const std::vector &input) { auto emptyLine = std::find(input.cbegin(), input.cend(), ""); auto beforeEmptyLine = std::vector(input.cbegin(), emptyLine); @@ -71,6 +98,14 @@ std::pair, std::vector> splitInput(const s std::move(beforeEmptyLine), std::move(afterEmptyLine)); } +/** + * Convert a single pattern to a grammar entry + * + * @param rawPattern The pattern from the entry to parse. + * + * @return MultiGrammarEntry The grammar entry to parse, which is a vector of single entries, each element + * representating an alternation. + */ MultiGrammarEntry parseSinglePattern(const std::string &rawPattern) { std::vector rawPatternComponents; folly::split(" ", rawPattern, rawPatternComponents); @@ -95,6 +130,14 @@ MultiGrammarEntry parseSinglePattern(const std::string &rawPattern) { return patternComponents; } +/** + * Parse the grammar part of the puzzle into a map of grammar entries. + * + * @param patterns The pattern from the entry to parse. + * + * @return std::unordered_multimap A map of grammar rule indices to grammar entries. Each + * element of the grammar entry vectors are alternations. + */ std::unordered_multimap parseGrammar(const std::vector &patterns) { std::unordered_multimap grammar; for (const std::string &patternLine : patterns) { @@ -117,20 +160,18 @@ std::unordered_multimap parseGrammar(const std::vector &grammar, int rule) { +/** + * Convert a grammar to a regular expression + * + * @param grammar The grammar for the puzzle + * @param rule The grammar rule to start the search at + * + * @return A regular expression for the puzzle input. + */ +std::string convertToRegularExpression(const std::unordered_multimap &grammar, int rule = 0) { std::vector expressions; auto ruleIterators = grammar.equal_range(rule); std::vector> entries(ruleIterators.first, ruleIterators.second); - for (const auto &entry : entries) { - auto cycleIt = std::find_if(entry.second.cbegin(), entry.second.cend(), [rule](const GrammarEntry &ruleEntry) { - return ruleEntry.isLookup() && ruleEntry.getIndex() == rule; - }); - - if (cycleIt != entry.second.cend()) { - entries = std::vector>{entry}; - break; - } - } std::transform( entries.cbegin(), entries.cend(), @@ -153,9 +194,9 @@ std::string convertToRegularExpression(const std::unordered_multimap fullSuffixElements; - for (int i = 1; i < 8; i++) { + // This is awful, but basically, checking for 31 11 42 is not posisble with a regular language, so we + // must check the possibilities of 1 of each, 2 of each, ... the value of NUM_RULE_11_CYCLES was picked + // arbitrarily because it works with my input. + for (int i = 1; i < NUM_RULE_11_CYCLES; i++) { fullSuffixElements.push_back( folly::format(suffixPrefix, i).str() + expression + folly::format(suffix, i).str()); } - std::cout << prefix << std::endl; - return prefix + "(" + folly::join("|", fullSuffixElements) + ")"; + return "(" + folly::join("|", fullSuffixElements) + ")"; } }); if (expressions.size() == 1) { return expressions.at(0); } else { + // Create an alternation of all of the entries return folly::format("(?:{})", folly::join("|", expressions)).str(); } } -int part1(const std::vector &patterns, const std::vector &testStrings) { - std::unordered_multimap grammar = parseGrammar(patterns); - std::regex inputRegex = std::regex(convertToRegularExpression(grammar, 0)); +/** + * Get the number of times the grammar matches the test strings + * + * @param grammar The grammar to check against + * @param testStrings The strings to test if they match + * + * @returns int The number of test strings that match the grammar + */ +int getNumberOfMatches( + const std::unordered_multimap &grammar, const std::vector &testStrings) { + std::regex inputRegex(convertToRegularExpression(grammar)); return std::count_if(testStrings.cbegin(), testStrings.cend(), [&inputRegex](const std::string &testString) { std::smatch matches; - if (std::regex_match(testString, matches, inputRegex)) { - std::cerr << testString << std::endl; - return true; - } - - return false; + return std::regex_match(testString, matches, inputRegex); }); } +int part1(const std::vector &patterns, const std::vector &testStrings) { + std::unordered_multimap grammar = parseGrammar(patterns); + return getNumberOfMatches(grammar, testStrings); +} + int part2(const std::vector &patterns, const std::vector &testStrings) { std::unordered_multimap grammar = parseGrammar(patterns); grammar.erase(8); @@ -204,26 +256,7 @@ int part2(const std::vector &patterns, const std::vector