summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt9
-rwxr-xr-xcoolvetica.ttfbin0 -> 135916 bytes
-rw-r--r--lingo.cpp173
3 files changed, 131 insertions, 51 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 56082ac..a67107b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -6,6 +6,7 @@ set(CMAKE_BUILD_TYPE Debug)
6find_package(PkgConfig) 6find_package(PkgConfig)
7pkg_check_modules(yaml-cpp yaml-cpp REQUIRED) 7pkg_check_modules(yaml-cpp yaml-cpp REQUIRED)
8pkg_check_modules(dpp dpp REQUIRED) 8pkg_check_modules(dpp dpp REQUIRED)
9pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED)
9 10
10add_subdirectory(vendor/curlcpp) 11add_subdirectory(vendor/curlcpp)
11add_subdirectory(vendor/verbly) 12add_subdirectory(vendor/verbly)
@@ -15,13 +16,15 @@ include_directories(
15 vendor/verbly/lib 16 vendor/verbly/lib
16 ${yaml-cpp_INCLUDE_DIRS} 17 ${yaml-cpp_INCLUDE_DIRS}
17 ${CURLCPP_SOURCE_DIR}/include 18 ${CURLCPP_SOURCE_DIR}/include
18 vendor/json) 19 vendor/json
20 ${GraphicsMagick_INCLUDE_DIRS})
19 21
20link_directories( 22link_directories(
21 ${dpp_LIBRARY_DIRS} 23 ${dpp_LIBRARY_DIRS}
22 ${yaml-cpp_LIBRARY_DIRS}) 24 ${yaml-cpp_LIBRARY_DIRS}
25 ${GraphicsMagick_LIBRARY_DIRS})
23 26
24add_executable(lingo lingo.cpp imagenet.cpp) 27add_executable(lingo lingo.cpp imagenet.cpp)
25set_property(TARGET lingo PROPERTY CXX_STANDARD 17) 28set_property(TARGET lingo PROPERTY CXX_STANDARD 17)
26set_property(TARGET lingo PROPERTY CXX_STANDARD_REQUIRED ON) 29set_property(TARGET lingo PROPERTY CXX_STANDARD_REQUIRED ON)
27target_link_libraries(lingo verbly ${dpp_LIBRARIES} ${yaml-cpp_LIBRARIES} curlcpp curl) 30target_link_libraries(lingo verbly ${dpp_LIBRARIES} ${yaml-cpp_LIBRARIES} curlcpp curl ${GraphicsMagick_LIBRARIES})
diff --git a/coolvetica.ttf b/coolvetica.ttf new file mode 100755 index 0000000..410ca31 --- /dev/null +++ b/coolvetica.ttf
Binary files differ
diff --git a/lingo.cpp b/lingo.cpp index be58e1c..61a1f71 100644 --- a/lingo.cpp +++ b/lingo.cpp
@@ -17,6 +17,7 @@
17#include <optional> 17#include <optional>
18#include <map> 18#include <map>
19#include <array> 19#include <array>
20#include <Magick++.h>
20#include "imagenet.h" 21#include "imagenet.h"
21 22
22#define ENABLE_BOT 23#define ENABLE_BOT
@@ -229,42 +230,50 @@ verbly::filter makeHintFilter(verbly::filter subfilter, Height height, Colour co
229 case kGreen: { 230 case kGreen: {
230 if (filter_direction == kTowardSolution) 231 if (filter_direction == kTowardSolution)
231 { 232 {
232 verbly::filter whitelist = 233 switch (height) {
233 (verbly::notion::wnid == 109287968) // Geological formations 234 case kBottom: {
234 || (verbly::notion::wnid == 109208496) // Asterisms (collections of stars) 235 verbly::filter whitelist =
235 || (verbly::notion::wnid == 109239740) // Celestial bodies 236 (verbly::notion::wnid == 109287968) // Geological formations
236 || (verbly::notion::wnid == 109277686) // Exterrestrial objects (comets and meteroids) 237 || (verbly::notion::wnid == 109208496) // Asterisms (collections of stars)
237 || (verbly::notion::wnid == 109403211) // Radiators (supposedly natural radiators but actually these are just pictures of radiators) 238 || (verbly::notion::wnid == 109239740) // Celestial bodies
238 || (verbly::notion::wnid == 109416076) // Rocks 239 || (verbly::notion::wnid == 109277686) // Exterrestrial objects (comets and meteroids)
239 || (verbly::notion::wnid == 105442131) // Chromosomes 240 || (verbly::notion::wnid == 109403211) // Radiators (supposedly natural radiators but actually these are just pictures of radiators)
240 || (verbly::notion::wnid == 100324978) // Tightrope walking 241 || (verbly::notion::wnid == 109416076) // Rocks
241 || (verbly::notion::wnid == 100326094) // Rock climbing 242 || (verbly::notion::wnid == 105442131) // Chromosomes
242 || (verbly::notion::wnid == 100433458) // Contact sports 243 || (verbly::notion::wnid == 100324978) // Tightrope walking
243 || (verbly::notion::wnid == 100433802) // Gymnastics 244 || (verbly::notion::wnid == 100326094) // Rock climbing
244 || (verbly::notion::wnid == 100439826) // Track and field 245 || (verbly::notion::wnid == 100433458) // Contact sports
245 || (verbly::notion::wnid == 100440747) // Skiing 246 || (verbly::notion::wnid == 100433802) // Gymnastics
246 || (verbly::notion::wnid == 100441824) // Water sport 247 || (verbly::notion::wnid == 100439826) // Track and field
247 || (verbly::notion::wnid == 100445351) // Rowing 248 || (verbly::notion::wnid == 100440747) // Skiing
248 || (verbly::notion::wnid == 100446980) // Archery 249 || (verbly::notion::wnid == 100441824) // Water sport
249 // TODO: add more sports 250 || (verbly::notion::wnid == 100445351) // Rowing
250 || (verbly::notion::wnid == 100021939) // Artifacts 251 || (verbly::notion::wnid == 100446980) // Archery
251 || (verbly::notion::wnid == 101471682) // Vertebrates 252 // TODO: add more sports
252 ; 253 || (verbly::notion::wnid == 100021939) // Artifacts
253 254 || (verbly::notion::wnid == 101471682) // Vertebrates
254 verbly::filter blacklist = 255 ;
255 (verbly::notion::wnid == 106883725) // swastika 256
256 || (verbly::notion::wnid == 104416901) // tetraskele 257 verbly::filter blacklist =
257 || (verbly::notion::wnid == 102512053) // fish 258 (verbly::notion::wnid == 106883725) // swastika
258 || (verbly::notion::wnid == 103575691) // instrument of execution 259 || (verbly::notion::wnid == 104416901) // tetraskele
259 || (verbly::notion::wnid == 103829563) // noose 260 || (verbly::notion::wnid == 102512053) // fish
260 || (verbly::notion::wnid == 103663910) // life support 261 || (verbly::notion::wnid == 103575691) // instrument of execution
261 ; 262 || (verbly::notion::wnid == 103829563) // noose
262 263 || (verbly::notion::wnid == 103663910) // life support
263 return subfilter 264 ;
264 && (verbly::notion::fullHypernyms %= whitelist) 265
265 && !(verbly::notion::fullHypernyms %= blacklist) 266 return subfilter
266 && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun) 267 && (verbly::notion::fullHypernyms %= whitelist)
267 && (verbly::notion::numOfImages >= 1); 268 && !(verbly::notion::fullHypernyms %= blacklist)
269 && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun)
270 && (verbly::notion::numOfImages >= 1);
271 }
272 case kMiddle: {
273 return subfilter;
274 }
275 default: break; // Never supported.
276 }
268 } else { 277 } else {
269 return (verbly::form::text == "picture"); 278 return (verbly::form::text == "picture");
270 } 279 }
@@ -420,6 +429,7 @@ public:
420 database_ = std::make_unique<verbly::database>(config["verbly_datafile"].as<std::string>()); 429 database_ = std::make_unique<verbly::database>(config["verbly_datafile"].as<std::string>());
421 imagenet_ = std::make_unique<imagenet>(config["imagenet"].as<std::string>()); 430 imagenet_ = std::make_unique<imagenet>(config["imagenet"].as<std::string>());
422 wanderlust_ = std::make_unique<wanderlust>(config["wanderlust"].as<std::string>()); 431 wanderlust_ = std::make_unique<wanderlust>(config["wanderlust"].as<std::string>());
432 fontpath_ = config["font"].as<std::string>();
423 433
424 scoreboard_endpoint_ = config["scoreboard_endpoint"].as<std::string>(); 434 scoreboard_endpoint_ = config["scoreboard_endpoint"].as<std::string>();
425 scoreboard_secret_code_ = config["scoreboard_secret_code"].as<std::string>(); 435 scoreboard_secret_code_ = config["scoreboard_secret_code"].as<std::string>();
@@ -519,6 +529,7 @@ private:
519 int moderate_uses = 0; 529 int moderate_uses = 0;
520 int green_uses = 0; 530 int green_uses = 0;
521 int orange_uses = 0; 531 int orange_uses = 0;
532 bool green_is_bottom = false;
522 std::array<std::optional<Colour>, kHeightCount> parts; 533 std::array<std::optional<Colour>, kHeightCount> parts;
523 for (int height = 0; height < static_cast<int>(kHeightCount); height++) { 534 for (int height = 0; height < static_cast<int>(kHeightCount); height++) {
524 if (std::bernoulli_distribution(0.5)(rng_)) { 535 if (std::bernoulli_distribution(0.5)(rng_)) {
@@ -547,6 +558,10 @@ private:
547 if (colour == kGreen) 558 if (colour == kGreen)
548 { 559 {
549 green_uses++; 560 green_uses++;
561 if (height == kBottom)
562 {
563 green_is_bottom = true;
564 }
550 } 565 }
551 if (colour == kOrange) 566 if (colour == kOrange)
552 { 567 {
@@ -563,9 +578,9 @@ private:
563 } 578 }
564 std::cout << std::endl; 579 std::cout << std::endl;
565 580
566 if (non_purple_uses < 1 || non_green_uses < 1) 581 if (non_purple_uses < 1 || (non_green_uses < 1 && green_is_bottom))
567 { 582 {
568 std::cout << "No hints (or only purple or only green hints)." << std::endl; 583 std::cout << "No hints (or only purple or only green bottom hints)." << std::endl;
569 continue; 584 continue;
570 } 585 }
571 if (expensive_uses > 1) 586 if (expensive_uses > 1)
@@ -672,16 +687,7 @@ private:
672 687
673 if (green_uses > 0) 688 if (green_uses > 0)
674 { 689 {
675 verbly::notion notion = database_->notions( 690 generateGreenPuzzle(solution, green_is_bottom, *genpuzzle);
676 (verbly::notion::numOfImages > 1) && solution).first();
677 auto [image, extension] = imagenet_->getImageForNotion(notion.getId(), rng_);
678 if (image.empty())
679 {
680 throw std::runtime_error("Could not find image for green hint.");
681 }
682
683 genpuzzle->attachment_name = std::string("SPOILER_image.") + extension;
684 genpuzzle->attachment_content = std::move(image);
685 } 691 }
686 692
687 made_puzzle = true; 693 made_puzzle = true;
@@ -790,6 +796,74 @@ private:
790 generation_thread.detach(); 796 generation_thread.detach();
791 } 797 }
792 798
799 void generateGreenPuzzle(const verbly::form& solution, bool is_bottom, puzzle& genpuzzle)
800 {
801 if (is_bottom)
802 {
803 verbly::notion notion = database_->notions(
804 (verbly::notion::numOfImages > 1) && solution).first();
805 auto [image, extension] = imagenet_->getImageForNotion(notion.getId(), rng_);
806 if (image.empty())
807 {
808 throw std::runtime_error("Could not find image for green hint.");
809 }
810
811 genpuzzle.attachment_name = std::string("SPOILER_image.") + extension;
812 genpuzzle.attachment_content = std::move(image);
813 } else {
814 double fontsize = 72;
815 std::string renderWord = solution.getText();
816
817 Magick::Image tester(Magick::Geometry(1, 1), "white");
818 tester.font(fontpath_);
819 tester.fontPointsize(fontsize);
820
821 Magick::TypeMetric metric;
822 tester.fontTypeMetrics(renderWord, &metric);
823
824 double imgHeight = metric.textHeight() * 2;
825 double imgWidth = metric.textWidth() * 2;
826
827 Magick::Image result(Magick::Geometry(imgWidth, imgHeight), "white");
828 result.font(fontpath_);
829 result.fontPointsize(fontsize);
830 result.draw(Magick::DrawableText(metric.textWidth() / 2, metric.textHeight() * 1.25, renderWord));
831 result.charcoal(2, 2);
832
833 Magick::Image lineimage(Magick::Geometry(imgWidth, imgHeight), "transparent");
834 lineimage.strokeColor("black");
835 lineimage.fillColor("black");
836
837 int lines = std::uniform_int_distribution<int>(1, 3)(rng_);
838 for (int i=0; i<lines; i++) {
839 lineimage.strokeWidth(std::uniform_int_distribution<int>(2, 6)(rng_));
840 lineimage.draw(Magick::DrawableLine(
841 std::uniform_int_distribution<int>(imgWidth / 10, imgWidth / 8)(rng_),
842 std::uniform_int_distribution<int>(imgHeight / 10, imgHeight - (imgHeight / 10))(rng_),
843 imgWidth - std::uniform_int_distribution<int>(imgWidth / 10, imgWidth / 8)(rng_),
844 std::uniform_int_distribution<int>(imgHeight / 10, imgHeight - (imgHeight / 10))(rng_)));
845 }
846
847 lineimage.gaussianBlur(std::uniform_int_distribution<int>(2,4)(rng_), std::uniform_int_distribution<int>(1,3)(rng_));
848 result.composite(lineimage, 0, 0, Magick::OverCompositeOp);
849
850 result.swirl(std::uniform_int_distribution<int>(45, 100)(rng_));
851 result.addNoise(Magick::NoiseType::GaussianNoise);
852 result.motionBlur(
853 std::uniform_int_distribution<int>(1,4)(rng_),
854 std::uniform_int_distribution<int>(1,4)(rng_),
855 std::uniform_int_distribution<int>(0, 360)(rng_));
856
857 result.magick("png");
858
859 Magick::Blob outputBlob;
860 result.write(&outputBlob);
861
862 genpuzzle.attachment_name = "image.png";
863 genpuzzle.attachment_content = std::string((const char*) outputBlob.data(), outputBlob.length());
864 }
865 }
866
793 std::mt19937& rng_; 867 std::mt19937& rng_;
794 std::unique_ptr<dpp::cluster> bot_; 868 std::unique_ptr<dpp::cluster> bot_;
795 std::unique_ptr<verbly::database> database_; 869 std::unique_ptr<verbly::database> database_;
@@ -797,6 +871,7 @@ private:
797 std::string scoreboard_endpoint_; 871 std::string scoreboard_endpoint_;
798 std::string scoreboard_secret_code_; 872 std::string scoreboard_secret_code_;
799 std::unique_ptr<wanderlust> wanderlust_; 873 std::unique_ptr<wanderlust> wanderlust_;
874 std::string fontpath_;
800 875
801 std::map<uint64_t, std::string> answer_by_message_; 876 std::map<uint64_t, std::string> answer_by_message_;
802 std::set<uint64_t> solved_puzzles_; 877 std::set<uint64_t> solved_puzzles_;
@@ -810,6 +885,8 @@ private:
810 885
811int main(int argc, char** argv) 886int main(int argc, char** argv)
812{ 887{
888 Magick::InitializeMagick(nullptr);
889
813 std::random_device randomDevice; 890 std::random_device randomDevice;
814 std::mt19937 rng{randomDevice()}; 891 std::mt19937 rng{randomDevice()};
815 892