From cebfd065c029788f3d6a8cc33b9401a708052335 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 15 Feb 2023 10:04:06 -0500 Subject: Did a buncha work --- generator.cpp | 675 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 generator.cpp (limited to 'generator.cpp') diff --git a/generator.cpp b/generator.cpp new file mode 100644 index 0000000..d0ef1a9 --- /dev/null +++ b/generator.cpp @@ -0,0 +1,675 @@ +#include "generator.h" +#include +#include + +verbly::filter Generator::MakeHintFilter(verbly::filter subfilter, Height height, Colour colour, FilterDirection filter_direction) +{ + switch (colour) { + case kWhite: { + switch (height) { + case kBottom: { + return (verbly::word::synonyms %= subfilter); + } + case kTop: { + return (verbly::form::pronunciations %= + verbly::filter("homophones", false, + (verbly::pronunciation::forms %= (subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))))); + } + case kMiddle: { + return subfilter; + } + default: break; // Not supported yet. + } + break; + } + case kBlack: { + switch (height) { + case kBottom: { + return (verbly::word::antonyms %= subfilter); + } + case kMiddle: { + return (verbly::form::antogram %= subfilter); + } + case kTop: { + return (verbly::pronunciation::antophone %= subfilter); + } + default: break; // Not supported yet. + } + break; + } + case kBrown: { + break; // Not supported yet. + } + case kRed: { + switch (height) { + case kTop: { + if (filter_direction == kTowardSolution) + { + return (verbly::pronunciation::merophones %= subfilter); + } else { + return (verbly::pronunciation::holophones %= subfilter); + } + } + case kMiddle: { + if (filter_direction == kTowardSolution) + { + return (verbly::form::merographs %= subfilter); + } else { + return (verbly::form::holographs %= subfilter); + } + } + case kBottom: { + if (filter_direction == kTowardSolution) + { + return (verbly::notion::partMeronyms %= + verbly::filter("partMeronyms", false, + subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); + } else { + return (verbly::notion::partHolonyms %= + verbly::filter("partHolonyms", false, + subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); + } + } + default: break; // Not supported yet. + } + break; + } + case kBlue: { + switch (height) { + case kTop: { + if (filter_direction == kTowardSolution) + { + return (verbly::pronunciation::holophones %= subfilter); + } else { + return (verbly::pronunciation::merophones %= subfilter); + } + } + case kMiddle: { + if (filter_direction == kTowardSolution) + { + return (verbly::form::holographs %= subfilter); + } else { + return (verbly::form::merographs %= subfilter); + } + } + case kBottom: { + if (filter_direction == kTowardSolution) + { + return (verbly::notion::partHolonyms %= + verbly::filter("partHolonyms", false, + subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); + } else { + return (verbly::notion::partMeronyms %= + verbly::filter("partMeronyms", false, + subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); + } + } + default: break; // Not supported yet. + } + break; + } + case kPurple: { + switch (height) { + case kMiddle: { + return (verbly::form::holographs %= + verbly::filter("midpurp", false, + (verbly::form::length >= 4 && (verbly::form::merographs %= + (subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id)))))); + } + case kTop: { + return (verbly::pronunciation::rhymes %= subfilter); + } + default: break; // Not supported yet. + } + break; + } + case kYellow: { + switch (height) { + case kTop: { + return (verbly::pronunciation::anaphones %= (subfilter && verbly::filter( + verbly::pronunciation::id, + verbly::filter::comparison::field_does_not_equal, + verbly::pronunciation::id))); + } + case kMiddle: { + return (verbly::form::anagrams %= (subfilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); + } + default: break; // Not supported yet. + } + break; + } + case kGreen: { + if (filter_direction == kTowardSolution) + { + switch (height) { + case kBottom: { + verbly::filter whitelist = + (verbly::notion::wnid == 109287968) // Geological formations + || (verbly::notion::wnid == 109208496) // Asterisms (collections of stars) + || (verbly::notion::wnid == 109239740) // Celestial bodies + || (verbly::notion::wnid == 109277686) // Exterrestrial objects (comets and meteroids) + || (verbly::notion::wnid == 109403211) // Radiators (supposedly natural radiators but actually these are just pictures of radiators) + || (verbly::notion::wnid == 109416076) // Rocks + || (verbly::notion::wnid == 105442131) // Chromosomes + || (verbly::notion::wnid == 100324978) // Tightrope walking + || (verbly::notion::wnid == 100326094) // Rock climbing + || (verbly::notion::wnid == 100433458) // Contact sports + || (verbly::notion::wnid == 100433802) // Gymnastics + || (verbly::notion::wnid == 100439826) // Track and field + || (verbly::notion::wnid == 100440747) // Skiing + || (verbly::notion::wnid == 100441824) // Water sport + || (verbly::notion::wnid == 100445351) // Rowing + || (verbly::notion::wnid == 100446980) // Archery + // TODO: add more sports + || (verbly::notion::wnid == 100021939) // Artifacts + || (verbly::notion::wnid == 101471682) // Vertebrates + ; + + verbly::filter blacklist = + (verbly::notion::wnid == 106883725) // swastika + || (verbly::notion::wnid == 104416901) // tetraskele + || (verbly::notion::wnid == 102512053) // fish + || (verbly::notion::wnid == 103575691) // instrument of execution + || (verbly::notion::wnid == 103829563) // noose + || (verbly::notion::wnid == 103663910) // life support + ; + + return subfilter + && (verbly::notion::fullHypernyms %= whitelist) + && !(verbly::notion::fullHypernyms %= blacklist) + && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun) + && (verbly::notion::numOfImages >= 1); + } + case kMiddle: { + return subfilter; + } + default: break; // Never supported. + } + } else { + return (verbly::form::text == "picture"); + } + break; + } + default: break; // Not supported yet. + } + return {}; +} + +std::string ApplyWanderlust(const std::string& word) { + std::string result; + for (char ch : word) { + if (ch == 'w') { + result += '1'; + } else if (ch == 'a') { + result += '2'; + } else if (ch == 'n') { + result += '3'; + } else if (ch == 'd') { + result += '4'; + } else if (ch == 'e') { + result += '5'; + } else if (ch == 'r') { + result += '6'; + } else if (ch == 'l') { + result += '7'; + } else if (ch == 'u') { + result += '8'; + } else if (ch == 's') { + result += '9'; + } else if (ch == 't') { + result += '0'; + } else if (ch == ' ') { + result += ' '; + } + } + return result; +} + +void Generator::GenerateStaticPanel(std::string name, std::string question, std::string answer) { + SavePanel(name, question, answer.empty() ? question : answer, {}); +} + +void Generator::GenerateSinglePanel(std::string name, Height height, Colour colour, GenerateOptions options) { + while (!GenerateSinglePanelImpl(name, height, colour, options)); +} + +bool Generator::GenerateSinglePanelImpl(std::string name, Height height, Colour colour, GenerateOptions options) { + verbly::form solution; + if (options.reuse_solution) { + const std::string& word = reusable_.at(std::uniform_int_distribution(0, reusable_.size()-1)(rng_)); + if (options.max_answer_len > 0 && word.size() > options.max_answer_len) return false; + solution = database_->forms(verbly::form::text == word).first(); + } else { + verbly::filter forward = MakeHintFilter({}, height, colour, kTowardSolution); + std::vector solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); + solution = solutions.front();//solutions.at(std::uniform_int_distribution(0, solutions.size())(rng_)); + } + + if (!options.unique_pool.empty() && pools_[options.unique_pool].count(solution.getText())) { + return false; + } + if (options.palindrome != kPalindromeUnspecified) { + std::string reversed = solution.getText(); + std::reverse(reversed.begin(), reversed.end()); + + if ((options.palindrome == kForcePalindrome && reversed != solution.getText()) || + (options.palindrome == kRejectPalindrome && reversed == solution.getText())) { + return false; + } + } + + // Finish early if this is a middle white. + if (height == kMiddle && colour == kWhite) { + SavePanel(name, solution.getText(), solution.getText(), options); + return true; + } + + verbly::filter questionFilter = MakeHintFilter(solution, height, colour, kTowardQuestion); + std::vector questions = database_->forms(questionFilter && GetWordFilter(kTowardQuestion, options)).all();//, verbly::order(verbly::form::id), 0).all(); + if (questions.size() < 1) return false; + verbly::form question = questions.front();// questions.at(std::uniform_int_distribution(0, questions.size())(rng_)); + + if (IsClueTrivial(height, colour, question, solution)) { + return false; + } + + SavePanel(name, question.getText(), solution.getText(), options); + if (!options.copy_to.empty()) { + SavePanel(options.copy_to, question.getText(), solution.getText(), options); + } + + return true; +} + +void Generator::GenerateDoublePanel(std::string name1, std::string name2, Height height, Colour colour, GenerateOptions options) { + while (!GenerateDoublePanelImpl(name1, name2, height, colour, options)); +} + +bool Generator::GenerateDoublePanelImpl(std::string name1, std::string name2, Height height, Colour colour, GenerateOptions options) { + verbly::form solution; + if (options.reuse_solution) { + const std::string& word = reusable_.at(std::uniform_int_distribution(0, reusable_.size()-1)(rng_)); + if (options.max_answer_len > 0 && word.size() > options.max_answer_len) return false; + solution = database_->forms(verbly::form::text == word).first(); + } else { + verbly::filter forward = MakeHintFilter({}, height, colour, kTowardSolution); + std::vector solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); + solution = solutions.front();//solutions.at(std::uniform_int_distribution(0, solutions.size())(rng_)); + } + + if (!options.unique_pool.empty() && pools_[options.unique_pool].count(solution.getText())) { + return false; + } + + // Finish early if this is a middle white. + if (height == kMiddle && colour == kWhite) { + SavePanel(name1, solution.getText(), solution.getText(), options); + SavePanel(name2, solution.getText(), solution.getText(), options); + return true; + } + + verbly::filter questionFilter = MakeHintFilter(solution, height, colour, kTowardQuestion); + std::vector questions = database_->forms(questionFilter && GetWordFilter(kTowardQuestion, options), {}, 2).all(); + if (questions.size() < 2) return false; + + //std::shuffle(questions.begin(), questions.end(), rng_); + + if (IsClueTrivial(height, colour, questions[0], solution) || IsClueTrivial(height, colour, questions[1], solution)) { + return false; + } + + SavePanel(name1, questions[0].getText(), solution.getText(), options); + SavePanel(name2, questions[1].getText(), solution.getText(), options); + + return true; +} + +void Generator::GenerateCohintedPanels(std::string name1, std::string name2, Height height, Colour colour, GenerateOptions options) { + while (!GenerateCohintedPanelsImpl(name1, name2, height, colour, options)); +} + +bool Generator::GenerateCohintedPanelsImpl(std::string name1, std::string name2, Height height, Colour colour, GenerateOptions options) { + verbly::filter backward = MakeHintFilter({}, height, colour, kTowardQuestion); + std::vector questions = database_->forms(backward && GetWordFilter(kTowardQuestion, options)).all();//, verbly::order(verbly::form::id), 0).all(); + verbly::form question = questions.front();// solutions.at(std::uniform_int_distribution(0, solutions.size())(rng_)); + + // Finish early if this is a middle white. + if (height == kMiddle && colour == kWhite) { + SavePanel(name1, question.getText(), question.getText(), options); + SavePanel(name2, question.getText(), question.getText(), options); + return true; + } + + verbly::filter solutionFilter = MakeHintFilter(question, height, colour, kTowardSolution); + std::vector solutions = database_->forms(solutionFilter && GetWordFilter(kTowardSolution, options), {}, 2).all(); + if (solutions.size() < 2) return false; + + //std::shuffle(questions.begin(), questions.end(), rng_); + + if (IsClueTrivial(height, colour, question, solutions[0]) || IsClueTrivial(height, colour, question, solutions[1])) { + return false; + } + + SavePanel(name1, question.getText(), solutions[0].getText(), options); + SavePanel(name2, question.getText(), solutions[1].getText(), options); + + return true; +} + +void Generator::GeneratePairedPanels(std::string name1, std::string name2, Height height, Colour colour, GenerateOptions options) { + while (!GeneratePairedPanelsImpl(name1, name2, height, colour, options)); +} + +bool Generator::GeneratePairedPanelsImpl(std::string name1, std::string name2, Height height, Colour colour, GenerateOptions options) { + Colour effectiveColour = (height == kMiddle && colour == kWhite) ? kBlack : colour; + Height effectiveHeight = (height == kMiddle && colour == kWhite) ? kBottom : height; + + verbly::filter forward = MakeHintFilter({}, effectiveHeight, effectiveColour, kTowardSolution); + std::vector solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); + verbly::form solution = solutions.front();//solutions.at(std::uniform_int_distribution(0, solutions.size())(rng_)); + + verbly::filter questionFilter = MakeHintFilter(solution, effectiveHeight, effectiveColour, kTowardQuestion); + std::vector questions = database_->forms(questionFilter && GetWordFilter(kTowardQuestion, options)).all();//, verbly::order(verbly::form::id), 0).all(); + if (questions.size() < 1) return false; + verbly::form question = questions.front();// questions.at(std::uniform_int_distribution(0, questions.size())(rng_)); + + if (IsClueTrivial(height, effectiveColour, question, solution)) { + return false; + } + + if (options.palindrome != kPalindromeUnspecified) { + std::string reversed = question.getText(); + std::reverse(reversed.begin(), reversed.end()); + + if ((options.palindrome == kForcePalindrome && reversed != question.getText()) || + (options.palindrome == kRejectPalindrome && reversed == question.getText())) { + return false; + } + } + + if (height == kMiddle && colour == kWhite) { + SavePanel(name1, question.getText(), question.getText(), options); + SavePanel(name2, solution.getText(), solution.getText(), options); + if (!options.copy_to.empty() && !options.copy_to2.empty()) { + SavePanel(options.copy_to, question.getText(), question.getText(), options); + SavePanel(options.copy_to2, solution.getText(), solution.getText(), options); + } + } else { + SavePanel(name1, question.getText(), solution.getText(), options); + SavePanel(name2, solution.getText(), question.getText(), options); + if (!options.copy_to.empty() && !options.copy_to2.empty()) { + SavePanel(options.copy_to, question.getText(), solution.getText(), options); + SavePanel(options.copy_to2, solution.getText(), question.getText(), options); + } + } + + return true; +} + +void Generator::GeneratePanelStack(std::string top_name, Colour top_colour, std::string middle_name, Colour middle_colour, std::string bottom_name, Colour bottom_colour, GenerateOptions options) { + while (!GeneratePanelStackImpl(top_name, top_colour, middle_name, middle_colour, bottom_name, bottom_colour, options)); +} + +bool Generator::GeneratePanelStackImpl(std::string top_name, Colour top_colour, std::string middle_name, Colour middle_colour, std::string bottom_name, Colour bottom_colour, GenerateOptions options) { + verbly::form solution; + if (options.reuse_solution) { + const std::string& word = reusable_.at(std::uniform_int_distribution(0, reusable_.size()-1)(rng_)); + if (options.max_answer_len > 0 && word.size() > options.max_answer_len) return false; + solution = database_->forms(verbly::form::text == word).first(); + } else { + verbly::filter forward = GetWordFilter(kTowardSolution, options); + if (!top_name.empty()) { + forward &= MakeHintFilter({}, kTop, top_colour, kTowardSolution); + } + if (!middle_name.empty()) { + forward &= MakeHintFilter({}, kMiddle, middle_colour, kTowardSolution); + } + if (!bottom_name.empty()) { + forward &= MakeHintFilter({}, kBottom, bottom_colour, kTowardSolution); + } + std::vector solutions = database_->forms(forward).all(); + solution = solutions.front();//solutions.at(std::uniform_int_distribution(0, solutions.size())(rng_)); + } + + if (!options.unique_pool.empty() && pools_[options.unique_pool].count(solution.getText())) { + return false; + } + + std::string top_hint; + std::string middle_hint; + std::string bottom_hint; + + if (!top_name.empty()) { + verbly::filter questionFilter = MakeHintFilter(solution, kTop, top_colour, kTowardQuestion); + std::vector questions = database_->forms(questionFilter && GetWordFilter(kTowardQuestion, options), {}, 1).all(); + if (questions.empty()) return false; + top_hint = questions.front().getText(); + + if (IsClueTrivial(kTop, top_colour, questions.front(), solution)) { + return false; + } + } + + if (!middle_name.empty()) { + verbly::filter questionFilter = MakeHintFilter(solution, kMiddle, middle_colour, kTowardQuestion); + std::vector questions = database_->forms(questionFilter && GetWordFilter(kTowardQuestion, options), {}, 1).all(); + if (questions.empty()) return false; + middle_hint = questions.front().getText(); + + if (IsClueTrivial(kMiddle, middle_colour, questions.front(), solution)) { + return false; + } + } + + if (!bottom_name.empty()) { + verbly::filter questionFilter = MakeHintFilter(solution, kBottom, bottom_colour, kTowardQuestion); + std::vector questions = database_->forms(questionFilter && GetWordFilter(kTowardQuestion, options), {}, 1).all(); + if (questions.empty()) return false; + bottom_hint = questions.front().getText(); + + if (IsClueTrivial(kBottom, bottom_colour, questions.front(), solution)) { + return false; + } + } + + if (!top_name.empty()) { + SavePanel(top_name, top_hint, solution.getText(), options); + } + if (!middle_name.empty()) { + SavePanel(middle_name, middle_hint, solution.getText(), options); + } + if (!bottom_name.empty()) { + SavePanel(bottom_name, bottom_hint, solution.getText(), options); + } + + return true; +} + +void Generator::GenerateOrangeNumberPanel(std::string name) { + std::string solution = wanderlust_->GetWord(rng_); + std::string question = ApplyWanderlust(solution); + + SavePanel(name, question, solution); +} + +void Generator::GenerateOrangeWordPanel(std::string name) { + std::string question = wanderlust_->GetWord(rng_); + std::string solution = ApplyWanderlust(question); + + SavePanel(name, question, solution); +} + +void Generator::GenerateOrangeAdditionPanel(std::string name) { + auto [question, solution] = wanderlust_->GetPuzzle(rng_); + + SavePanel(name, question, solution); +} + +void Generator::GenerateOneRoadManyTurns(std::string order_name, std::string part1_name, std::string part2_name, std::string part3_name, std::string part4_name) { + const auto& [part1_q, part1_a] = panels_.at(part1_name); + const auto& [part2_q, part2_a] = panels_.at(part2_name); + const auto& [part3_q, part3_a] = panels_.at(part3_name); + const auto& [part4_q, part4_a] = panels_.at(part4_name); + + SavePanel(order_name, "order", part1_a + " " + part2_a + " " + part3_a + " " + part4_a); +} + +void Generator::GenerateComboPanel(std::string name, Height left_height, Colour left_colour, Height right_height, Colour right_colour, GenerateOptions options) { + while (!GenerateComboPanelImpl(name, left_height, left_colour, right_height, right_colour, options)); +} + +bool Generator::GenerateComboPanelImpl(std::string name, Height left_height, Colour left_colour, Height right_height, Colour right_colour, GenerateOptions options) { + options.force_two_words = true; + + verbly::form solution = database_->forms(GetWordFilter(kTowardSolution, options)).first(); + std::string soltext = solution.getText(); + int spacepos = soltext.find(" "); + std::string leftword = soltext.substr(0, spacepos); + std::string rightword = soltext.substr(spacepos+1); + + options.force_two_words = false; + verbly::filter left_filter = MakeHintFilter(verbly::form::text == leftword, left_height, left_colour, kTowardQuestion); + std::vector left_questions = database_->forms(left_filter && GetWordFilter(kTowardQuestion, options)).all(); + if (left_questions.size() < 1) return false; + verbly::form left_question = left_questions.front(); + + verbly::filter right_filter = MakeHintFilter(verbly::form::text == rightword, right_height, right_colour, kTowardQuestion); + std::vector right_questions = database_->forms(right_filter && GetWordFilter(kTowardQuestion, options)).all(); + if (right_questions.size() < 1) return false; + verbly::form right_question = right_questions.front(); + + SavePanel(name, left_question.getText() + " " + right_question.getText(), soltext, options); + + return true; +} + +void Generator::GenerateCrossTower( + std::string north_tower_name, + std::string south_tower_name, + std::string east_tower_name, + std::string west_tower_name, + std::string north_lookout_name, + std::string south_lookout_name, + std::string east_lookout_name, + std::string west_lookout_name, + std::string north_other_name1, + std::string north_other_name2, + std::string north_other_name3, + std::string south_other_name1, + std::string south_other_name2, + std::string south_other_name3, + std::string east_other_name1, + std::string east_other_name2, + std::string east_other_name3, + std::string west_other_name1, + std::string west_other_name2, + std::string west_other_name3) +{ + std::vector> sets = cross_tower_->GetPuzzleSet(rng_); + +} + +void Generator::SavePanel(std::string name, std::string question, std::string answer, GenerateOptions options) { + if (options.save_for_later) { + reusable_.push_back(answer); + } + + if (!options.unique_pool.empty()) { + pools_[options.unique_pool].insert(answer); + } + + if (options.obscure_hint) { + int numToObscure = (question.size()/3 > 0) ? std::uniform_int_distribution(1, question.size()/3)(rng_) : 1; + std::vector indicies(question.size()); + std::iota(indicies.begin(), indicies.end(), 0); + std::shuffle(indicies.begin(), indicies.end(), rng_); + + for (int i=0; i 0) { + wordFilter &= (verbly::form::length == options.exact_len); + } else if (dir == kTowardSolution && options.max_answer_len > 0) { + wordFilter &= (verbly::form::length <= options.max_answer_len); + } else if (dir == kTowardQuestion && options.max_hint_len > 0) { + wordFilter &= (verbly::form::length <= options.max_hint_len); + } else { + wordFilter &= (verbly::form::length <= 11); + } + + if (options.exact_len == 0) { + wordFilter &= (verbly::form::length >= 3); + } + + if (!options.multiword) { + if (options.force_two_words) { + wordFilter &= (verbly::form::complexity == 2); + } else { + wordFilter &= (verbly::form::complexity == 1); + } + } else { + wordFilter &= ((verbly::form::complexity > 1) || (verbly::form::frequency > 2000000)); + } + + return wordFilter; +} + +bool Generator::IsClueTrivial(Height height, Colour colour, const verbly::form& clue, const verbly::form& solution) const +{ + if (height == kTop && colour == kWhite) + { + return !database_->forms((verbly::filter)clue && (verbly::word::synonyms %= solution)).all().empty(); + } else if (height == kBottom && colour == kWhite) + { + return !database_->forms((verbly::filter)clue && (verbly::form::pronunciations %= solution)).all().empty(); + } else if (height == kBottom && colour == kBlack) + { + return !database_->forms((verbly::filter)clue && (verbly::form::merographs %= solution)).all().empty() + || !database_->forms((verbly::filter)clue && (verbly::form::holographs %= solution)).all().empty(); + } else if ((height == kMiddle || height == kTop) && colour == kPurple) + { + return (clue.getId() == solution.getId()) + || !database_->forms((verbly::filter)clue && (verbly::form::merographs %= solution)).all().empty() + || !database_->forms((verbly::filter)clue && (verbly::form::holographs %= solution)).all().empty(); + } + return false; +} -- cgit 1.4.1