Clean up day 19

master
Nick Krichevsky 2020-12-21 18:05:26 -05:00
parent f6ed34b9f9
commit ebdc9989eb
1 changed files with 80 additions and 47 deletions

View File

@ -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<GrammarEntry>;
using AlternatableMultiGrammarEntry = std::vector<MultiGrammarEntry>;
/**
* 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<std::string> 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<std::string>, std::vector<std::string>> A pair of the puzzle grammar and the puzzle
* test strings
*/
std::pair<std::vector<std::string>, std::vector<std::string>> splitInput(const std::vector<std::string> &input) {
auto emptyLine = std::find(input.cbegin(), input.cend(), "");
auto beforeEmptyLine = std::vector<std::string>(input.cbegin(), emptyLine);
@ -71,6 +98,14 @@ std::pair<std::vector<std::string>, std::vector<std::string>> 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<std::string> 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<int, MultiGrammarEntry> A map of grammar rule indices to grammar entries. Each
* element of the grammar entry vectors are alternations.
*/
std::unordered_multimap<int, MultiGrammarEntry> parseGrammar(const std::vector<std::string> &patterns) {
std::unordered_multimap<int, MultiGrammarEntry> grammar;
for (const std::string &patternLine : patterns) {
@ -117,20 +160,18 @@ std::unordered_multimap<int, MultiGrammarEntry> parseGrammar(const std::vector<s
return grammar;
}
std::string convertToRegularExpression(const std::unordered_multimap<int, MultiGrammarEntry> &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<int, MultiGrammarEntry> &grammar, int rule = 0) {
std::vector<std::string> expressions;
auto ruleIterators = grammar.equal_range(rule);
std::vector<std::pair<int, MultiGrammarEntry>> 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<std::pair<int, MultiGrammarEntry>>{entry};
break;
}
}
std::transform(
entries.cbegin(),
entries.cend(),
@ -153,9 +194,9 @@ std::string convertToRegularExpression(const std::unordered_multimap<int, MultiG
isNonRegularCycle = true;
// This grossness will be resolved when using a format string. We want to be able to replace this
// with a number later.
suffixPrefix = folly::format("((?:{}){{{{{{}}}}}})((?:", expression).str();
suffixPrefix = folly::format("(?:{}){{{{{{}}}}}}(?:", expression).str();
expression.clear();
suffix = "){{{}}})";
suffix = "){{{}}}";
} else {
expression += convertToRegularExpression(grammar, grammarEntry.getIndex());
}
@ -165,37 +206,48 @@ std::string convertToRegularExpression(const std::unordered_multimap<int, MultiG
return expression;
} else {
std::vector<std::string> 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<std::string> &patterns, const std::vector<std::string> &testStrings) {
std::unordered_multimap<int, MultiGrammarEntry> 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<int, MultiGrammarEntry> &grammar, const std::vector<std::string> &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<std::string> &patterns, const std::vector<std::string> &testStrings) {
std::unordered_multimap<int, MultiGrammarEntry> grammar = parseGrammar(patterns);
return getNumberOfMatches(grammar, testStrings);
}
int part2(const std::vector<std::string> &patterns, const std::vector<std::string> &testStrings) {
std::unordered_multimap<int, MultiGrammarEntry> grammar = parseGrammar(patterns);
grammar.erase(8);
@ -204,26 +256,7 @@ int part2(const std::vector<std::string> &patterns, const std::vector<std::strin
grammar.emplace(8, MultiGrammarEntry{GrammarEntry(42), GrammarEntry(8)});
grammar.emplace(11, MultiGrammarEntry{GrammarEntry(42), GrammarEntry(31)});
grammar.emplace(11, MultiGrammarEntry{GrammarEntry(42), GrammarEntry(11), GrammarEntry(31)});
std::regex inputRegex(convertToRegularExpression(grammar, 0));
std::regex rule42Regex(convertToRegularExpression(grammar, 42));
std::regex rule31Regex(convertToRegularExpression(grammar, 31));
std::cout << "Expressions" << std::endl;
std::cout << "0" << std::endl;
std::cout << convertToRegularExpression(grammar, 0) << std::endl;
std::cout << "42" << std::endl;
std::cout << convertToRegularExpression(grammar, 42) << std::endl;
std::cout << "31" << std::endl;
std::cout << convertToRegularExpression(grammar, 31) << std::endl;
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 getNumberOfMatches(grammar, testStrings);
}
int main(int argc, char *argv[]) {
@ -235,6 +268,6 @@ int main(int argc, char *argv[]) {
auto input = readInput(argv[1]);
auto parsedInput = splitInput(input);
// std::cout << part1(parsedInput.first, parsedInput.second) << std::endl;
std::cout << part1(parsedInput.first, parsedInput.second) << std::endl;
std::cout << part2(parsedInput.first, parsedInput.second) << std::endl;
}