From 7b19e08dc5665753bcdc99b33968939ea47d981a Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 8 Dec 2022 17:03:32 -0500 Subject: Added /newpuzzle slash command --- lingo.cpp | 548 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 298 insertions(+), 250 deletions(-) diff --git a/lingo.cpp b/lingo.cpp index b417ffb..3b60c82 100644 --- a/lingo.cpp +++ b/lingo.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -41,231 +42,132 @@ const std::string COLOUR_EMOJIS[kColourCount] = { "🟨" }; -int main(int argc, char** argv) -{ - std::random_device randomDevice; - std::mt19937 rng{randomDevice()}; +class lingo { +public: + lingo(std::mt19937& rng) : rng_(rng) {} - if (argc != 2) + void run(const std::string& configpath) { - std::cout << "usage: lingo [configfile]" << std::endl; - return -1; - } + YAML::Node config = YAML::LoadFile(configpath); + bot_ = std::make_unique(config["discord_token"].as()); - std::string configfile(argv[1]); - YAML::Node config = YAML::LoadFile(configfile); + bot_->on_ready([this](const dpp::ready_t& event) { + if (dpp::run_once()) + { + dpp::slashcommand newcommand("newpuzzle", "Generate a new LINGO puzzle", bot_->me.id); + bot_->global_command_create(newcommand); + } + }); - std::map answer_by_message; - std::mutex answers_mutex; + bot_->on_slashcommand([this](const dpp::slashcommand_t& event) { + if (event.command.get_command_name() == "newpuzzle") { + event.thinking(true); - dpp::cluster bot(config["discord_token"].as()); - bot.on_message_create([&bot, &answers_mutex, &answer_by_message](const dpp::message_create_t& event) { - std::lock_guard answer_lock(answers_mutex); - if (answer_by_message.count(event.msg.message_reference.message_id)) - { - std::string canonical_answer = hatkirby::lowercase(answer_by_message[event.msg.message_reference.message_id]); - std::string canonical_attempt = hatkirby::lowercase(event.msg.content); - while (canonical_attempt.find("||") != std::string::npos) - { - canonical_attempt.erase(canonical_attempt.find("||"), 2); + std::thread genpuzz(&lingo::generatePuzzle, this, event.command.get_channel().id); + genpuzz.detach(); } + }); - if (canonical_attempt == canonical_answer) + bot_->on_message_create([this](const dpp::message_create_t& event) { + std::lock_guard answer_lock(answers_mutex_); + if (answer_by_message_.count(event.msg.message_reference.message_id)) { - bot.message_add_reaction(event.msg.id, event.msg.channel_id, "✅"); - } else { - bot.message_add_reaction(event.msg.id, event.msg.channel_id, "❌"); + std::string canonical_answer = hatkirby::lowercase(answer_by_message_[event.msg.message_reference.message_id]); + std::string canonical_attempt = hatkirby::lowercase(event.msg.content); + while (canonical_attempt.find("||") != std::string::npos) + { + canonical_attempt.erase(canonical_attempt.find("||"), 2); + } + + if (canonical_attempt == canonical_answer) + { + bot_->message_add_reaction(event.msg.id, event.msg.channel_id, "✅"); + } else { + bot_->message_add_reaction(event.msg.id, event.msg.channel_id, "❌"); + } } + }); + + bot_->start(); + + dpp::snowflake channel(config["discord_channel"].as()); + + database_ = std::make_unique(config["verbly_datafile"].as()); + + for (;;) + { + std::thread genpuzz(&lingo::generatePuzzle, this, channel); + genpuzz.detach(); + + std::this_thread::sleep_for(std::chrono::hours(24)); } - }); - bot.start(); - - dpp::snowflake channel(config["discord_channel"].as()); - - verbly::database database(config["verbly_datafile"].as()); - - std::set> filters = { - {kTop, kPurple}, - {kTop, kWhite}, - {kTop, kYellow}, - {kTop, kRed}, - {kTop, kBlue}, - {kMiddle, kYellow}, - {kMiddle, kRed}, - {kMiddle, kBlue}, - {kMiddle, kPurple}, - {kBottom, kWhite}, - {kBottom, kBlack}, - {kBottom, kRed}, - {kBottom, kBlue}, - }; - - verbly::filter wordFilter = (verbly::form::proper == false); - - verbly::filter cleanFilter = - !(verbly::word::usageDomains %= (verbly::notion::wnid == 106718862)) // ethnic slurs - && !(verbly::notion::wnid == 110630093); // "spastic" - - for (;;) + } + +private: + + void generatePuzzle(dpp::snowflake channel) { - try - { - int hints = 0; - std::array, kHeightCount> parts; - for (int height = 0; height < static_cast(kHeightCount); height++) { - if (std::bernoulli_distribution(0.5)(rng)) { - int colour = std::uniform_int_distribution(0, static_cast(kColourCount)-1)(rng); - if (filters.count({static_cast(height), static_cast(colour)})) { - parts[static_cast(height)] = static_cast(colour); - hints++; - } - } - } + std::set> filters = { + {kTop, kPurple}, + {kTop, kWhite}, + {kTop, kYellow}, + {kTop, kRed}, + {kTop, kBlue}, + {kMiddle, kYellow}, + {kMiddle, kRed}, + {kMiddle, kBlue}, + {kMiddle, kPurple}, + {kBottom, kWhite}, + {kBottom, kBlack}, + {kBottom, kRed}, + {kBottom, kBlue}, + }; - if (hints < 2) { - continue; - } + verbly::filter wordFilter = (verbly::form::proper == false); - verbly::filter forwardFilter = cleanFilter && (verbly::form::proper == false); - for (int i=0; i(kHeightCount); i++) { - Height height = static_cast(i); - std::optional& colour = parts[i]; - if (!colour.has_value()) { - continue; - } - switch (*colour) { - case kWhite: { - switch (height) { - case kBottom: { - forwardFilter &= (verbly::word::synonyms %= wordFilter); - break; - } - case kTop: { - forwardFilter &= (verbly::form::pronunciations %= - verbly::filter("homophones", false, - (verbly::pronunciation::forms %= (wordFilter && verbly::filter( - verbly::form::id, - verbly::filter::comparison::field_does_not_equal, - verbly::form::id))))); - break; - } - default: break; // Not supposed yet. - } - break; - } - case kBlack: { - switch (height) { - case kBottom: { - forwardFilter &= (verbly::word::antonyms %= wordFilter); - break; - } - default: break; // Not supposed yet. - } - break; - } - case kBrown: { - switch (height) { - case kBottom: { - forwardFilter &= (verbly::notion::causes %= wordFilter); - break; - } - default: break; // Not supposed yet. - } - break; - } - case kRed: { - switch (height) { - case kTop: { - forwardFilter &= (verbly::pronunciation::holophones %= wordFilter); - break; - } - case kMiddle: { - forwardFilter &= (verbly::form::holographs %= wordFilter); - break; - } - case kBottom: { - forwardFilter &= (verbly::notion::partMeronyms %= wordFilter); - break; - } - default: break; // Not supposed yet. - } - break; - } - case kBlue: { - switch (height) { - case kTop: { - forwardFilter &= (verbly::pronunciation::merophones %= wordFilter); - break; - } - case kMiddle: { - forwardFilter &= (verbly::form::merographs %= wordFilter); - break; - } - case kBottom: { - forwardFilter &= (verbly::notion::partHolonyms %= wordFilter); - break; - } - default: break; // Not supposed yet. - } - break; - } - case kPurple: { - switch (height) { - case kMiddle: { - forwardFilter &= (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= wordFilter))); - break; - } - case kTop: { - forwardFilter &= (verbly::pronunciation::rhymes %= wordFilter); - break; - } - default: break; // Not supposed yet. - } - break; - } - case kYellow: { - switch (height) { - case kTop: { - forwardFilter &= (verbly::pronunciation::anaphones %= (wordFilter && verbly::filter( - verbly::pronunciation::id, - verbly::filter::comparison::field_does_not_equal, - verbly::pronunciation::id))); - break; - } - case kMiddle: { - forwardFilter &= (verbly::form::anagrams %= (wordFilter && verbly::filter( - verbly::form::id, - verbly::filter::comparison::field_does_not_equal, - verbly::form::id))); - break; - } - default: break; // Not supposed yet. + verbly::filter cleanFilter = + !(verbly::word::usageDomains %= (verbly::notion::wnid == 106718862)) // ethnic slurs + && !(verbly::notion::wnid == 110630093); // "spastic" + + bool generated = false; + while (!generated) + { + try + { + int hints = 0; + std::array, kHeightCount> parts; + for (int height = 0; height < static_cast(kHeightCount); height++) { + if (std::bernoulli_distribution(0.5)(rng_)) { + int colour = std::uniform_int_distribution(0, static_cast(kColourCount)-1)(rng_); + if (filters.count({static_cast(height), static_cast(colour)})) { + parts[static_cast(height)] = static_cast(colour); + hints++; } - break; } - default: break; // Not supposed yet. } - } - verbly::form solution = database.forms(forwardFilter).first(); + if (hints < 2) { + continue; + } - std::ostringstream msg_stream; - for (int i=0; i(kHeightCount); i++) { - Height height = static_cast(i); - std::optional& colour = parts[i]; - if (colour.has_value()) { - verbly::filter questionFilter; + verbly::filter forwardFilter = cleanFilter && (verbly::form::proper == false); + for (int i=0; i(kHeightCount); i++) { + Height height = static_cast(i); + std::optional& colour = parts[i]; + if (!colour.has_value()) { + continue; + } switch (*colour) { case kWhite: { switch (height) { case kBottom: { - questionFilter = (verbly::word::synonyms %= solution); + forwardFilter &= (verbly::word::synonyms %= wordFilter); break; } case kTop: { - questionFilter = (verbly::form::pronunciations %= + forwardFilter &= (verbly::form::pronunciations %= verbly::filter("homophones", false, - (verbly::pronunciation::forms %= ((verbly::filter)solution && verbly::filter( + (verbly::pronunciation::forms %= (wordFilter && verbly::filter( verbly::form::id, verbly::filter::comparison::field_does_not_equal, verbly::form::id))))); @@ -278,7 +180,7 @@ int main(int argc, char** argv) case kBlack: { switch (height) { case kBottom: { - questionFilter = (verbly::word::antonyms %= solution); + forwardFilter &= (verbly::word::antonyms %= wordFilter); break; } default: break; // Not supposed yet. @@ -288,51 +190,43 @@ int main(int argc, char** argv) case kBrown: { switch (height) { case kBottom: { - questionFilter = (verbly::notion::effects %= solution); + forwardFilter &= (verbly::notion::causes %= wordFilter); break; } default: break; // Not supposed yet. } break; } - case kBlue: { + case kRed: { switch (height) { case kTop: { - questionFilter = (verbly::pronunciation::holophones %= solution); + forwardFilter &= (verbly::pronunciation::holophones %= wordFilter); break; } case kMiddle: { - questionFilter = (verbly::form::holographs %= solution); + forwardFilter &= (verbly::form::holographs %= wordFilter); break; } case kBottom: { - /*questionFilter = ((verbly::notion::fullMemberHolonyms %= solution) - || (verbly::notion::fullPartHolonyms %= solution) - || (verbly::notion::fullSubstanceHolonyms %= solution));*/ - //questionFilter &= !(verbly::notion::words %= solution); - questionFilter = (verbly::notion::partMeronyms %= solution); + forwardFilter &= (verbly::notion::partMeronyms %= wordFilter); break; } default: break; // Not supposed yet. } break; } - case kRed: { + case kBlue: { switch (height) { case kTop: { - questionFilter = (verbly::pronunciation::merophones %= solution); + forwardFilter &= (verbly::pronunciation::merophones %= wordFilter); break; } case kMiddle: { - questionFilter = (verbly::form::merographs %= solution); + forwardFilter &= (verbly::form::merographs %= wordFilter); break; } case kBottom: { - /*questionFilter = ((verbly::notion::fullMemberMeronyms %= solution) - || (verbly::notion::fullPartMeronyms %= solution) - || (verbly::notion::fullSubstanceMeronyms %= solution));*/ - questionFilter = (verbly::notion::partHolonyms %= solution); - //questionFilter &= !(verbly::notion::words %= solution); + forwardFilter &= (verbly::notion::partHolonyms %= wordFilter); break; } default: break; // Not supposed yet. @@ -341,12 +235,12 @@ int main(int argc, char** argv) } case kPurple: { switch (height) { - case kTop: { - questionFilter = (verbly::pronunciation::rhymes %= solution); + case kMiddle: { + forwardFilter &= (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= wordFilter))); break; } - case kMiddle: { - questionFilter = (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= solution))); + case kTop: { + forwardFilter &= (verbly::pronunciation::rhymes %= wordFilter); break; } default: break; // Not supposed yet. @@ -356,17 +250,17 @@ int main(int argc, char** argv) case kYellow: { switch (height) { case kTop: { - questionFilter = (verbly::pronunciation::anaphones %= ((verbly::filter)solution && verbly::filter( - verbly::pronunciation::id, - verbly::filter::comparison::field_does_not_equal, - verbly::pronunciation::id))); + forwardFilter &= (verbly::pronunciation::anaphones %= (wordFilter && verbly::filter( + verbly::pronunciation::id, + verbly::filter::comparison::field_does_not_equal, + verbly::pronunciation::id))); break; } case kMiddle: { - questionFilter = (verbly::form::anagrams %= ((verbly::filter)solution && verbly::filter( - verbly::form::id, - verbly::filter::comparison::field_does_not_equal, - verbly::form::id))); + forwardFilter &= (verbly::form::anagrams %= (wordFilter && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); break; } default: break; // Not supposed yet. @@ -375,33 +269,187 @@ int main(int argc, char** argv) } default: break; // Not supposed yet. } - verbly::form questionPart = database.forms(questionFilter && cleanFilter).first(); - msg_stream << COLOUR_EMOJIS[*colour] << " " << questionPart.getText() << std::endl; - } else { - msg_stream << "▪️" << std::endl; } - } - msg_stream << "(" << solution.getText().size() << ")"; - std::string message_text = msg_stream.str(); - std::cout << message_text << std::endl << std::endl << solution.getText() << std::endl; + verbly::form solution = database_->forms(forwardFilter).first(); - dpp::message message(channel, message_text); - bot.message_create(message, [&answers_mutex, &answer_by_message, &solution](const dpp::confirmation_callback_t& userdata) { - const auto& posted_msg = std::get(userdata.value); - std::lock_guard answer_lock(answers_mutex); - if (answer_by_message.size() > 3000) - { - answer_by_message.clear(); + std::ostringstream msg_stream; + for (int i=0; i(kHeightCount); i++) { + Height height = static_cast(i); + std::optional& colour = parts[i]; + if (colour.has_value()) { + verbly::filter questionFilter; + switch (*colour) { + case kWhite: { + switch (height) { + case kBottom: { + questionFilter = (verbly::word::synonyms %= solution); + break; + } + case kTop: { + questionFilter = (verbly::form::pronunciations %= + verbly::filter("homophones", false, + (verbly::pronunciation::forms %= ((verbly::filter)solution && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))))); + break; + } + default: break; // Not supposed yet. + } + break; + } + case kBlack: { + switch (height) { + case kBottom: { + questionFilter = (verbly::word::antonyms %= solution); + break; + } + default: break; // Not supposed yet. + } + break; + } + case kBrown: { + switch (height) { + case kBottom: { + questionFilter = (verbly::notion::effects %= solution); + break; + } + default: break; // Not supposed yet. + } + break; + } + case kBlue: { + switch (height) { + case kTop: { + questionFilter = (verbly::pronunciation::holophones %= solution); + break; + } + case kMiddle: { + questionFilter = (verbly::form::holographs %= solution); + break; + } + case kBottom: { + /*questionFilter = ((verbly::notion::fullMemberHolonyms %= solution) + || (verbly::notion::fullPartHolonyms %= solution) + || (verbly::notion::fullSubstanceHolonyms %= solution));*/ + //questionFilter &= !(verbly::notion::words %= solution); + questionFilter = (verbly::notion::partMeronyms %= solution); + break; + } + default: break; // Not supposed yet. + } + break; + } + case kRed: { + switch (height) { + case kTop: { + questionFilter = (verbly::pronunciation::merophones %= solution); + break; + } + case kMiddle: { + questionFilter = (verbly::form::merographs %= solution); + break; + } + case kBottom: { + /*questionFilter = ((verbly::notion::fullMemberMeronyms %= solution) + || (verbly::notion::fullPartMeronyms %= solution) + || (verbly::notion::fullSubstanceMeronyms %= solution));*/ + questionFilter = (verbly::notion::partHolonyms %= solution); + //questionFilter &= !(verbly::notion::words %= solution); + break; + } + default: break; // Not supposed yet. + } + break; + } + case kPurple: { + switch (height) { + case kTop: { + questionFilter = (verbly::pronunciation::rhymes %= solution); + break; + } + case kMiddle: { + questionFilter = (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= solution))); + break; + } + default: break; // Not supposed yet. + } + break; + } + case kYellow: { + switch (height) { + case kTop: { + questionFilter = (verbly::pronunciation::anaphones %= ((verbly::filter)solution && verbly::filter( + verbly::pronunciation::id, + verbly::filter::comparison::field_does_not_equal, + verbly::pronunciation::id))); + break; + } + case kMiddle: { + questionFilter = (verbly::form::anagrams %= ((verbly::filter)solution && verbly::filter( + verbly::form::id, + verbly::filter::comparison::field_does_not_equal, + verbly::form::id))); + break; + } + default: break; // Not supposed yet. + } + break; + } + default: break; // Not supposed yet. + } + verbly::form questionPart = database_->forms(questionFilter && cleanFilter).first(); + msg_stream << COLOUR_EMOJIS[*colour] << " " << questionPart.getText() << std::endl; + } else { + msg_stream << "▪️" << std::endl; + } } - answer_by_message[posted_msg.id] = solution.getText(); - }); + msg_stream << "(" << solution.getText().size() << ")"; + + std::string message_text = msg_stream.str(); + std::cout << message_text << std::endl << std::endl << solution.getText() << std::endl; - std::this_thread::sleep_for(std::chrono::hours(3)); - } catch (const std::exception& ex) { - std::cout << ex.what() << std::endl; + dpp::message message(channel, message_text); + bot_->message_create(message, [this, &solution](const dpp::confirmation_callback_t& userdata) { + const auto& posted_msg = std::get(userdata.value); + std::lock_guard answer_lock(answers_mutex_); + if (answer_by_message_.size() > 3000) + { + answer_by_message_.clear(); + } + answer_by_message_[posted_msg.id] = solution.getText(); + }); + + generated = true; + } catch (const std::exception& ex) { + std::cout << ex.what() << std::endl; + } + + std::this_thread::sleep_for(std::chrono::minutes(1)); } + } + + std::mt19937& rng_; + std::unique_ptr bot_; + std::unique_ptr database_; + std::map answer_by_message_; + std::mutex answers_mutex_; +}; + +int main(int argc, char** argv) +{ + std::random_device randomDevice; + std::mt19937 rng{randomDevice()}; - std::this_thread::sleep_for(std::chrono::minutes(1)); + if (argc != 2) + { + std::cout << "usage: lingo [configfile]" << std::endl; + return -1; } + + lingo lingo(rng); + lingo.run(argv[1]); + + return 0; } -- cgit 1.4.1