summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--lingo.cpp413
2 files changed, 232 insertions, 189 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f09bbd..38883b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -5,21 +5,21 @@ set(CMAKE_BUILD_TYPE Debug)
5 5
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(mastodonpp mastodonpp REQUIRED) 8pkg_check_modules(dpp dpp REQUIRED)
9 9
10add_subdirectory(vendor/verbly) 10add_subdirectory(vendor/verbly)
11 11
12include_directories( 12include_directories(
13 ${mastodonpp_INCLUDE_DIRS} 13 ${dpp_INCLUDE_DIRS}
14 vendor/verbly/lib 14 vendor/verbly/lib
15 ${yaml-cpp_INCLUDE_DIRS} 15 ${yaml-cpp_INCLUDE_DIRS}
16 vendor/json) 16 vendor/json)
17 17
18link_directories( 18link_directories(
19 ${mastodonpp_LIBRARY_DIRS} 19 ${dpp_LIBRARY_DIRS}
20 ${yaml-cpp_LIBRARY_DIRS}) 20 ${yaml-cpp_LIBRARY_DIRS})
21 21
22add_executable(lingo lingo.cpp) 22add_executable(lingo lingo.cpp)
23set_property(TARGET lingo PROPERTY CXX_STANDARD 17) 23set_property(TARGET lingo PROPERTY CXX_STANDARD 17)
24set_property(TARGET lingo PROPERTY CXX_STANDARD_REQUIRED ON) 24set_property(TARGET lingo PROPERTY CXX_STANDARD_REQUIRED ON)
25target_link_libraries(lingo verbly ${mastodonpp_LIBRARIES} ${yaml-cpp_LIBRARIES}) 25target_link_libraries(lingo verbly ${dpp_LIBRARIES} ${yaml-cpp_LIBRARIES})
diff --git a/lingo.cpp b/lingo.cpp index b549804..b417ffb 100644 --- a/lingo.cpp +++ b/lingo.cpp
@@ -1,8 +1,9 @@
1#include <mastodonpp/mastodonpp.hpp> 1#include <dpp/dpp.h>
2#include <random> 2#include <random>
3#include <yaml-cpp/yaml.h> 3#include <yaml-cpp/yaml.h>
4#include <iostream> 4#include <iostream>
5#include <thread> 5#include <thread>
6#include <mutex>
6#include <chrono> 7#include <chrono>
7#include <string> 8#include <string>
8#include <algorithm> 9#include <algorithm>
@@ -54,12 +55,34 @@ int main(int argc, char** argv)
54 std::string configfile(argv[1]); 55 std::string configfile(argv[1]);
55 YAML::Node config = YAML::LoadFile(configfile); 56 YAML::Node config = YAML::LoadFile(configfile);
56 57
57 verbly::database database(config["verbly_datafile"].as<std::string>()); 58 std::map<uint64_t, std::string> answer_by_message;
59 std::mutex answers_mutex;
60
61 dpp::cluster bot(config["discord_token"].as<std::string>());
62 bot.on_message_create([&bot, &answers_mutex, &answer_by_message](const dpp::message_create_t& event) {
63 std::lock_guard answer_lock(answers_mutex);
64 if (answer_by_message.count(event.msg.message_reference.message_id))
65 {
66 std::string canonical_answer = hatkirby::lowercase(answer_by_message[event.msg.message_reference.message_id]);
67 std::string canonical_attempt = hatkirby::lowercase(event.msg.content);
68 while (canonical_attempt.find("||") != std::string::npos)
69 {
70 canonical_attempt.erase(canonical_attempt.find("||"), 2);
71 }
72
73 if (canonical_attempt == canonical_answer)
74 {
75 bot.message_add_reaction(event.msg.id, event.msg.channel_id, "✅");
76 } else {
77 bot.message_add_reaction(event.msg.id, event.msg.channel_id, "❌");
78 }
79 }
80 });
81 bot.start();
58 82
59 /*mastodonpp::Instance instance{ 83 dpp::snowflake channel(config["discord_channel"].as<uint64_t>());
60 config["mastodon_instance"].as<std::string>(), 84
61 config["mastodon_token"].as<std::string>()}; 85 verbly::database database(config["verbly_datafile"].as<std::string>());
62 mastodonpp::Connection connection{instance};*/
63 86
64 std::set<std::tuple<Height, Colour>> filters = { 87 std::set<std::tuple<Height, Colour>> filters = {
65 {kTop, kPurple}, 88 {kTop, kPurple},
@@ -85,161 +108,42 @@ int main(int argc, char** argv)
85 108
86 for (;;) 109 for (;;)
87 { 110 {
88 bool puzzleEmpty = true; 111 try
89 std::array<std::optional<Colour>, kHeightCount> parts; 112 {
90 for (int height = 0; height < static_cast<int>(kHeightCount); height++) { 113 int hints = 0;
91 if (std::bernoulli_distribution(0.5)(rng)) { 114 std::array<std::optional<Colour>, kHeightCount> parts;
92 int colour = std::uniform_int_distribution<int>(0, static_cast<int>(kColourCount)-1)(rng); 115 for (int height = 0; height < static_cast<int>(kHeightCount); height++) {
93 if (filters.count({static_cast<Height>(height), static_cast<Colour>(colour)})) { 116 if (std::bernoulli_distribution(0.5)(rng)) {
94 parts[static_cast<Height>(height)] = static_cast<Colour>(colour); 117 int colour = std::uniform_int_distribution<int>(0, static_cast<int>(kColourCount)-1)(rng);
95 puzzleEmpty = false; 118 if (filters.count({static_cast<Height>(height), static_cast<Colour>(colour)})) {
119 parts[static_cast<Height>(height)] = static_cast<Colour>(colour);
120 hints++;
121 }
96 } 122 }
97 } 123 }
98 }
99 124
100 if (puzzleEmpty) { 125 if (hints < 2) {
101 continue;
102 }
103
104 verbly::filter forwardFilter = cleanFilter && (verbly::form::proper == false);
105 for (int i=0; i<static_cast<int>(kHeightCount); i++) {
106 Height height = static_cast<Height>(i);
107 std::optional<Colour>& colour = parts[i];
108 if (!colour.has_value()) {
109 continue; 126 continue;
110 } 127 }
111 switch (*colour) {
112 case kWhite: {
113 switch (height) {
114 case kBottom: {
115 forwardFilter &= (verbly::word::synonyms %= wordFilter);
116 break;
117 }
118 case kTop: {
119 forwardFilter &= (verbly::form::pronunciations %=
120 verbly::filter("homophones", false,
121 (verbly::pronunciation::forms %= (wordFilter && verbly::filter(
122 verbly::form::id,
123 verbly::filter::comparison::field_does_not_equal,
124 verbly::form::id)))));
125 break;
126 }
127 default: break; // Not supposed yet.
128 }
129 break;
130 }
131 case kBlack: {
132 switch (height) {
133 case kBottom: {
134 forwardFilter &= (verbly::word::antonyms %= wordFilter);
135 break;
136 }
137 default: break; // Not supposed yet.
138 }
139 break;
140 }
141 case kBrown: {
142 switch (height) {
143 case kBottom: {
144 forwardFilter &= (verbly::notion::causes %= wordFilter);
145 break;
146 }
147 default: break; // Not supposed yet.
148 }
149 break;
150 }
151 case kRed: {
152 switch (height) {
153 case kTop: {
154 forwardFilter &= (verbly::pronunciation::holophones %= wordFilter);
155 break;
156 }
157 case kMiddle: {
158 forwardFilter &= (verbly::form::holographs %= wordFilter);
159 break;
160 }
161 case kBottom: {
162 forwardFilter &= (verbly::notion::partMeronyms %= wordFilter);
163 break;
164 }
165 default: break; // Not supposed yet.
166 }
167 break;
168 }
169 case kBlue: {
170 switch (height) {
171 case kTop: {
172 forwardFilter &= (verbly::pronunciation::merophones %= wordFilter);
173 break;
174 }
175 case kMiddle: {
176 forwardFilter &= (verbly::form::merographs %= wordFilter);
177 break;
178 }
179 case kBottom: {
180 forwardFilter &= (verbly::notion::partHolonyms %= wordFilter);
181 break;
182 }
183 default: break; // Not supposed yet.
184 }
185 break;
186 }
187 case kPurple: {
188 switch (height) {
189 case kMiddle: {
190 forwardFilter &= (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= wordFilter)));
191 break;
192 }
193 case kTop: {
194 forwardFilter &= (verbly::pronunciation::rhymes %= wordFilter);
195 break;
196 }
197 default: break; // Not supposed yet.
198 }
199 break;
200 }
201 case kYellow: {
202 switch (height) {
203 case kTop: {
204 forwardFilter &= (verbly::pronunciation::anaphones %= (wordFilter && verbly::filter(
205 verbly::pronunciation::id,
206 verbly::filter::comparison::field_does_not_equal,
207 verbly::pronunciation::id)));
208 break;
209 }
210 case kMiddle: {
211 forwardFilter &= (verbly::form::anagrams %= (wordFilter && verbly::filter(
212 verbly::form::id,
213 verbly::filter::comparison::field_does_not_equal,
214 verbly::form::id)));
215 break;
216 }
217 default: break; // Not supposed yet.
218 }
219 break;
220 }
221 default: break; // Not supposed yet.
222 }
223 }
224 128
225 verbly::form solution = database.forms(forwardFilter).first(); 129 verbly::filter forwardFilter = cleanFilter && (verbly::form::proper == false);
226 130 for (int i=0; i<static_cast<int>(kHeightCount); i++) {
227 for (int i=0; i<static_cast<int>(kHeightCount); i++) { 131 Height height = static_cast<Height>(i);
228 Height height = static_cast<Height>(i); 132 std::optional<Colour>& colour = parts[i];
229 std::optional<Colour>& colour = parts[i]; 133 if (!colour.has_value()) {
230 if (colour.has_value()) { 134 continue;
231 verbly::filter questionFilter; 135 }
232 switch (*colour) { 136 switch (*colour) {
233 case kWhite: { 137 case kWhite: {
234 switch (height) { 138 switch (height) {
235 case kBottom: { 139 case kBottom: {
236 questionFilter = (verbly::word::synonyms %= solution); 140 forwardFilter &= (verbly::word::synonyms %= wordFilter);
237 break; 141 break;
238 } 142 }
239 case kTop: { 143 case kTop: {
240 questionFilter = (verbly::form::pronunciations %= 144 forwardFilter &= (verbly::form::pronunciations %=
241 verbly::filter("homophones", false, 145 verbly::filter("homophones", false,
242 (verbly::pronunciation::forms %= ((verbly::filter)solution && verbly::filter( 146 (verbly::pronunciation::forms %= (wordFilter && verbly::filter(
243 verbly::form::id, 147 verbly::form::id,
244 verbly::filter::comparison::field_does_not_equal, 148 verbly::filter::comparison::field_does_not_equal,
245 verbly::form::id))))); 149 verbly::form::id)))));
@@ -252,7 +156,7 @@ int main(int argc, char** argv)
252 case kBlack: { 156 case kBlack: {
253 switch (height) { 157 switch (height) {
254 case kBottom: { 158 case kBottom: {
255 questionFilter = (verbly::word::antonyms %= solution); 159 forwardFilter &= (verbly::word::antonyms %= wordFilter);
256 break; 160 break;
257 } 161 }
258 default: break; // Not supposed yet. 162 default: break; // Not supposed yet.
@@ -262,51 +166,43 @@ int main(int argc, char** argv)
262 case kBrown: { 166 case kBrown: {
263 switch (height) { 167 switch (height) {
264 case kBottom: { 168 case kBottom: {
265 questionFilter = (verbly::notion::effects %= solution); 169 forwardFilter &= (verbly::notion::causes %= wordFilter);
266 break; 170 break;
267 } 171 }
268 default: break; // Not supposed yet. 172 default: break; // Not supposed yet.
269 } 173 }
270 break; 174 break;
271 } 175 }
272 case kBlue: { 176 case kRed: {
273 switch (height) { 177 switch (height) {
274 case kTop: { 178 case kTop: {
275 questionFilter = (verbly::pronunciation::holophones %= solution); 179 forwardFilter &= (verbly::pronunciation::holophones %= wordFilter);
276 break; 180 break;
277 } 181 }
278 case kMiddle: { 182 case kMiddle: {
279 questionFilter = (verbly::form::holographs %= solution); 183 forwardFilter &= (verbly::form::holographs %= wordFilter);
280 break; 184 break;
281 } 185 }
282 case kBottom: { 186 case kBottom: {
283 /*questionFilter = ((verbly::notion::fullMemberHolonyms %= solution) 187 forwardFilter &= (verbly::notion::partMeronyms %= wordFilter);
284 || (verbly::notion::fullPartHolonyms %= solution)
285 || (verbly::notion::fullSubstanceHolonyms %= solution));*/
286 //questionFilter &= !(verbly::notion::words %= solution);
287 questionFilter = (verbly::notion::partMeronyms %= solution);
288 break; 188 break;
289 } 189 }
290 default: break; // Not supposed yet. 190 default: break; // Not supposed yet.
291 } 191 }
292 break; 192 break;
293 } 193 }
294 case kRed: { 194 case kBlue: {
295 switch (height) { 195 switch (height) {
296 case kTop: { 196 case kTop: {
297 questionFilter = (verbly::pronunciation::merophones %= solution); 197 forwardFilter &= (verbly::pronunciation::merophones %= wordFilter);
298 break; 198 break;
299 } 199 }
300 case kMiddle: { 200 case kMiddle: {
301 questionFilter = (verbly::form::merographs %= solution); 201 forwardFilter &= (verbly::form::merographs %= wordFilter);
302 break; 202 break;
303 } 203 }
304 case kBottom: { 204 case kBottom: {
305 /*questionFilter = ((verbly::notion::fullMemberMeronyms %= solution) 205 forwardFilter &= (verbly::notion::partHolonyms %= wordFilter);
306 || (verbly::notion::fullPartMeronyms %= solution)
307 || (verbly::notion::fullSubstanceMeronyms %= solution));*/
308 questionFilter = (verbly::notion::partHolonyms %= solution);
309 //questionFilter &= !(verbly::notion::words %= solution);
310 break; 206 break;
311 } 207 }
312 default: break; // Not supposed yet. 208 default: break; // Not supposed yet.
@@ -315,12 +211,12 @@ int main(int argc, char** argv)
315 } 211 }
316 case kPurple: { 212 case kPurple: {
317 switch (height) { 213 switch (height) {
318 case kTop: { 214 case kMiddle: {
319 questionFilter = (verbly::pronunciation::rhymes %= solution); 215 forwardFilter &= (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= wordFilter)));
320 break; 216 break;
321 } 217 }
322 case kMiddle: { 218 case kTop: {
323 questionFilter = (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= solution))); 219 forwardFilter &= (verbly::pronunciation::rhymes %= wordFilter);
324 break; 220 break;
325 } 221 }
326 default: break; // Not supposed yet. 222 default: break; // Not supposed yet.
@@ -330,17 +226,17 @@ int main(int argc, char** argv)
330 case kYellow: { 226 case kYellow: {
331 switch (height) { 227 switch (height) {
332 case kTop: { 228 case kTop: {
333 questionFilter = (verbly::pronunciation::anaphones %= ((verbly::filter)solution && verbly::filter( 229 forwardFilter &= (verbly::pronunciation::anaphones %= (wordFilter && verbly::filter(
334 verbly::pronunciation::id, 230 verbly::pronunciation::id,
335 verbly::filter::comparison::field_does_not_equal, 231 verbly::filter::comparison::field_does_not_equal,
336 verbly::pronunciation::id))); 232 verbly::pronunciation::id)));
337 break; 233 break;
338 } 234 }
339 case kMiddle: { 235 case kMiddle: {
340 questionFilter = (verbly::form::anagrams %= ((verbly::filter)solution && verbly::filter( 236 forwardFilter &= (verbly::form::anagrams %= (wordFilter && verbly::filter(
341 verbly::form::id, 237 verbly::form::id,
342 verbly::filter::comparison::field_does_not_equal, 238 verbly::filter::comparison::field_does_not_equal,
343 verbly::form::id))); 239 verbly::form::id)));
344 break; 240 break;
345 } 241 }
346 default: break; // Not supposed yet. 242 default: break; // Not supposed yet.
@@ -349,16 +245,163 @@ int main(int argc, char** argv)
349 } 245 }
350 default: break; // Not supposed yet. 246 default: break; // Not supposed yet.
351 } 247 }
352 verbly::form questionPart = database.forms(questionFilter && cleanFilter).first();
353 std::cout << COLOUR_EMOJIS[*colour] << " " << questionPart.getText() << std::endl;
354 } else {
355 std::cout << "▪️" << std::endl;
356 } 248 }
357 }
358 std::cout << "(" << solution.getText().size() << ")" << std::endl << std::endl << solution.getText() << std::endl;
359 249
250 verbly::form solution = database.forms(forwardFilter).first();
251
252 std::ostringstream msg_stream;
253 for (int i=0; i<static_cast<int>(kHeightCount); i++) {
254 Height height = static_cast<Height>(i);
255 std::optional<Colour>& colour = parts[i];
256 if (colour.has_value()) {
257 verbly::filter questionFilter;
258 switch (*colour) {
259 case kWhite: {
260 switch (height) {
261 case kBottom: {
262 questionFilter = (verbly::word::synonyms %= solution);
263 break;
264 }
265 case kTop: {
266 questionFilter = (verbly::form::pronunciations %=
267 verbly::filter("homophones", false,
268 (verbly::pronunciation::forms %= ((verbly::filter)solution && verbly::filter(
269 verbly::form::id,
270 verbly::filter::comparison::field_does_not_equal,
271 verbly::form::id)))));
272 break;
273 }
274 default: break; // Not supposed yet.
275 }
276 break;
277 }
278 case kBlack: {
279 switch (height) {
280 case kBottom: {
281 questionFilter = (verbly::word::antonyms %= solution);
282 break;
283 }
284 default: break; // Not supposed yet.
285 }
286 break;
287 }
288 case kBrown: {
289 switch (height) {
290 case kBottom: {
291 questionFilter = (verbly::notion::effects %= solution);
292 break;
293 }
294 default: break; // Not supposed yet.
295 }
296 break;
297 }
298 case kBlue: {
299 switch (height) {
300 case kTop: {
301 questionFilter = (verbly::pronunciation::holophones %= solution);
302 break;
303 }
304 case kMiddle: {
305 questionFilter = (verbly::form::holographs %= solution);
306 break;
307 }
308 case kBottom: {
309 /*questionFilter = ((verbly::notion::fullMemberHolonyms %= solution)
310 || (verbly::notion::fullPartHolonyms %= solution)
311 || (verbly::notion::fullSubstanceHolonyms %= solution));*/
312 //questionFilter &= !(verbly::notion::words %= solution);
313 questionFilter = (verbly::notion::partMeronyms %= solution);
314 break;
315 }
316 default: break; // Not supposed yet.
317 }
318 break;
319 }
320 case kRed: {
321 switch (height) {
322 case kTop: {
323 questionFilter = (verbly::pronunciation::merophones %= solution);
324 break;
325 }
326 case kMiddle: {
327 questionFilter = (verbly::form::merographs %= solution);
328 break;
329 }
330 case kBottom: {
331 /*questionFilter = ((verbly::notion::fullMemberMeronyms %= solution)
332 || (verbly::notion::fullPartMeronyms %= solution)
333 || (verbly::notion::fullSubstanceMeronyms %= solution));*/
334 questionFilter = (verbly::notion::partHolonyms %= solution);
335 //questionFilter &= !(verbly::notion::words %= solution);
336 break;
337 }
338 default: break; // Not supposed yet.
339 }
340 break;
341 }
342 case kPurple: {
343 switch (height) {
344 case kTop: {
345 questionFilter = (verbly::pronunciation::rhymes %= solution);
346 break;
347 }
348 case kMiddle: {
349 questionFilter = (verbly::form::merographs %= (verbly::form::length >= 4 && (verbly::form::holographs %= solution)));
350 break;
351 }
352 default: break; // Not supposed yet.
353 }
354 break;
355 }
356 case kYellow: {
357 switch (height) {
358 case kTop: {
359 questionFilter = (verbly::pronunciation::anaphones %= ((verbly::filter)solution && verbly::filter(
360 verbly::pronunciation::id,
361 verbly::filter::comparison::field_does_not_equal,
362 verbly::pronunciation::id)));
363 break;
364 }
365 case kMiddle: {
366 questionFilter = (verbly::form::anagrams %= ((verbly::filter)solution && verbly::filter(
367 verbly::form::id,
368 verbly::filter::comparison::field_does_not_equal,
369 verbly::form::id)));
370 break;
371 }
372 default: break; // Not supposed yet.
373 }
374 break;
375 }
376 default: break; // Not supposed yet.
377 }
378 verbly::form questionPart = database.forms(questionFilter && cleanFilter).first();
379 msg_stream << COLOUR_EMOJIS[*colour] << " " << questionPart.getText() << std::endl;
380 } else {
381 msg_stream << "▪️" << std::endl;
382 }
383 }
384 msg_stream << "(" << solution.getText().size() << ")";
385
386 std::string message_text = msg_stream.str();
387 std::cout << message_text << std::endl << std::endl << solution.getText() << std::endl;
388
389 dpp::message message(channel, message_text);
390 bot.message_create(message, [&answers_mutex, &answer_by_message, &solution](const dpp::confirmation_callback_t& userdata) {
391 const auto& posted_msg = std::get<dpp::message>(userdata.value);
392 std::lock_guard answer_lock(answers_mutex);
393 if (answer_by_message.size() > 3000)
394 {
395 answer_by_message.clear();
396 }
397 answer_by_message[posted_msg.id] = solution.getText();
398 });
399
400 std::this_thread::sleep_for(std::chrono::hours(3));
401 } catch (const std::exception& ex) {
402 std::cout << ex.what() << std::endl;
403 }
360 404
361 // We can poll the timeline at most once every five minutes. 405 std::this_thread::sleep_for(std::chrono::minutes(1));
362 std::this_thread::sleep_for(std::chrono::hours(3));
363 } 406 }
364} 407}