diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2023-02-17 09:51:36 -0500 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2023-02-17 09:51:36 -0500 |
commit | 26aa11444ca03571a2bf1ea14d54402094b5c15f (patch) | |
tree | 2c1d3b7ad2d5608007245d552eac187829aabfef /generator.cpp | |
parent | cebfd065c029788f3d6a8cc33b9401a708052335 (diff) | |
download | lingo-randomizer-26aa11444ca03571a2bf1ea14d54402094b5c15f.tar.gz lingo-randomizer-26aa11444ca03571a2bf1ea14d54402094b5c15f.tar.bz2 lingo-randomizer-26aa11444ca03571a2bf1ea14d54402094b5c15f.zip |
many changes (should stop doing this)
max_len_diff is an attempt to make mid blue and mid purple more reasonable. It maybe works for blue, but it is not good enough for purple. what we will need to do is limit on how much of the word can be removed, and that's going to require a change to verbly to allow that kind of query. copy_hidden was added for the champion's rest colour pairs. The solution to the pair is found at champion's rest, and the corresponding colour pair has a hint that is just question marks. must_be_broad forces a generated word to be targettable by bottom white/red/blue. this + reusing solution for most bottom white/red/blue puzzles makes those puzzles much more reasonable (though still hard). an attempt to randomise cross tower was made, but I'm not happy with it. hyphens in obscured hints will no longer be obscured. I think obscured hints tend to be too obscured sometimes though, especially when it comes to the number hunt and uncommon words. middle red triviality check: middle red two word puzzles should not just remove one of the words middle yellow triviality check: middle yellow two word puzzles should not just swap the words top yellow triviality check: top yellow should not be able to swap two phonemes that are identical other than stress and say that it rearranged the word. the colorful is partially randomised. champion's rest is as randomsied as it could be (it is likely not reasonable to randomise the red/blue/yellow panels behind the walls. the fearless is partially randomised (besides the chain puzzles). the challenge is partially randomised.
Diffstat (limited to 'generator.cpp')
-rw-r--r-- | generator.cpp | 106 |
1 files changed, 104 insertions, 2 deletions
diff --git a/generator.cpp b/generator.cpp index d0ef1a9..c4dd76f 100644 --- a/generator.cpp +++ b/generator.cpp | |||
@@ -1,6 +1,9 @@ | |||
1 | #include "generator.h" | 1 | #include "generator.h" |
2 | #include <algorithm> | 2 | #include <algorithm> |
3 | #include <iostream> | 3 | #include <iostream> |
4 | #include <cstdlib> | ||
5 | #include <cctype> | ||
6 | #include <hkutil/string.h> | ||
4 | 7 | ||
5 | verbly::filter Generator::MakeHintFilter(verbly::filter subfilter, Height height, Colour colour, FilterDirection filter_direction) | 8 | verbly::filter Generator::MakeHintFilter(verbly::filter subfilter, Height height, Colour colour, FilterDirection filter_direction) |
6 | { | 9 | { |
@@ -262,6 +265,11 @@ bool Generator::GenerateSinglePanelImpl(std::string name, Height height, Colour | |||
262 | solution = database_->forms(verbly::form::text == word).first(); | 265 | solution = database_->forms(verbly::form::text == word).first(); |
263 | } else { | 266 | } else { |
264 | verbly::filter forward = MakeHintFilter({}, height, colour, kTowardSolution); | 267 | verbly::filter forward = MakeHintFilter({}, height, colour, kTowardSolution); |
268 | if (options.must_be_broad) { | ||
269 | forward &= MakeHintFilter({}, kBottom, kWhite, kTowardSolution); | ||
270 | forward &= MakeHintFilter({}, kBottom, kRed, kTowardSolution); | ||
271 | forward &= MakeHintFilter({}, kBottom, kBlue, kTowardSolution); | ||
272 | } | ||
265 | std::vector<verbly::form> solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); | 273 | std::vector<verbly::form> solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); |
266 | solution = solutions.front();//solutions.at(std::uniform_int_distribution<int>(0, solutions.size())(rng_)); | 274 | solution = solutions.front();//solutions.at(std::uniform_int_distribution<int>(0, solutions.size())(rng_)); |
267 | } | 275 | } |
@@ -282,6 +290,12 @@ bool Generator::GenerateSinglePanelImpl(std::string name, Height height, Colour | |||
282 | // Finish early if this is a middle white. | 290 | // Finish early if this is a middle white. |
283 | if (height == kMiddle && colour == kWhite) { | 291 | if (height == kMiddle && colour == kWhite) { |
284 | SavePanel(name, solution.getText(), solution.getText(), options); | 292 | SavePanel(name, solution.getText(), solution.getText(), options); |
293 | if (!options.copy_to.empty()) { | ||
294 | SavePanel(options.copy_to, solution.getText(), solution.getText(), options); | ||
295 | } | ||
296 | if (!options.copy_hidden.empty()) { | ||
297 | SavePanel(options.copy_hidden, std::string(solution.getText().size(), '?'), solution.getText(), options); | ||
298 | } | ||
285 | return true; | 299 | return true; |
286 | } | 300 | } |
287 | 301 | ||
@@ -293,6 +307,9 @@ bool Generator::GenerateSinglePanelImpl(std::string name, Height height, Colour | |||
293 | if (IsClueTrivial(height, colour, question, solution)) { | 307 | if (IsClueTrivial(height, colour, question, solution)) { |
294 | return false; | 308 | return false; |
295 | } | 309 | } |
310 | if (options.max_len_diff >= 0 && std::abs(static_cast<int>(question.getText().size() - solution.getText().size())) > options.max_len_diff) { | ||
311 | return false; | ||
312 | } | ||
296 | 313 | ||
297 | SavePanel(name, question.getText(), solution.getText(), options); | 314 | SavePanel(name, question.getText(), solution.getText(), options); |
298 | if (!options.copy_to.empty()) { | 315 | if (!options.copy_to.empty()) { |
@@ -314,6 +331,11 @@ bool Generator::GenerateDoublePanelImpl(std::string name1, std::string name2, He | |||
314 | solution = database_->forms(verbly::form::text == word).first(); | 331 | solution = database_->forms(verbly::form::text == word).first(); |
315 | } else { | 332 | } else { |
316 | verbly::filter forward = MakeHintFilter({}, height, colour, kTowardSolution); | 333 | verbly::filter forward = MakeHintFilter({}, height, colour, kTowardSolution); |
334 | if (options.must_be_broad) { | ||
335 | forward &= MakeHintFilter({}, kBottom, kWhite, kTowardSolution); | ||
336 | forward &= MakeHintFilter({}, kBottom, kRed, kTowardSolution); | ||
337 | forward &= MakeHintFilter({}, kBottom, kBlue, kTowardSolution); | ||
338 | } | ||
317 | std::vector<verbly::form> solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); | 339 | std::vector<verbly::form> solutions = database_->forms(forward && GetWordFilter(kTowardSolution, options)).all(); |
318 | solution = solutions.front();//solutions.at(std::uniform_int_distribution<int>(0, solutions.size())(rng_)); | 340 | solution = solutions.front();//solutions.at(std::uniform_int_distribution<int>(0, solutions.size())(rng_)); |
319 | } | 341 | } |
@@ -338,6 +360,12 @@ bool Generator::GenerateDoublePanelImpl(std::string name1, std::string name2, He | |||
338 | if (IsClueTrivial(height, colour, questions[0], solution) || IsClueTrivial(height, colour, questions[1], solution)) { | 360 | if (IsClueTrivial(height, colour, questions[0], solution) || IsClueTrivial(height, colour, questions[1], solution)) { |
339 | return false; | 361 | return false; |
340 | } | 362 | } |
363 | if (options.max_len_diff >= 0) { | ||
364 | if (std::abs(static_cast<int>(questions[0].getText().size() - solution.getText().size())) > options.max_len_diff | ||
365 | || std::abs(static_cast<int>(questions[1].getText().size() - solution.getText().size())) > options.max_len_diff) { | ||
366 | return false; | ||
367 | } | ||
368 | } | ||
341 | 369 | ||
342 | SavePanel(name1, questions[0].getText(), solution.getText(), options); | 370 | SavePanel(name1, questions[0].getText(), solution.getText(), options); |
343 | SavePanel(name2, questions[1].getText(), solution.getText(), options); | 371 | SavePanel(name2, questions[1].getText(), solution.getText(), options); |
@@ -370,6 +398,12 @@ bool Generator::GenerateCohintedPanelsImpl(std::string name1, std::string name2, | |||
370 | if (IsClueTrivial(height, colour, question, solutions[0]) || IsClueTrivial(height, colour, question, solutions[1])) { | 398 | if (IsClueTrivial(height, colour, question, solutions[0]) || IsClueTrivial(height, colour, question, solutions[1])) { |
371 | return false; | 399 | return false; |
372 | } | 400 | } |
401 | if (options.max_len_diff >= 0) { | ||
402 | if (std::abs(static_cast<int>(question.getText().size() - solutions[0].getText().size())) > options.max_len_diff | ||
403 | || std::abs(static_cast<int>(question.getText().size() - solutions[1].getText().size())) > options.max_len_diff) { | ||
404 | return false; | ||
405 | } | ||
406 | } | ||
373 | 407 | ||
374 | SavePanel(name1, question.getText(), solutions[0].getText(), options); | 408 | SavePanel(name1, question.getText(), solutions[0].getText(), options); |
375 | SavePanel(name2, question.getText(), solutions[1].getText(), options); | 409 | SavePanel(name2, question.getText(), solutions[1].getText(), options); |
@@ -408,6 +442,12 @@ bool Generator::GeneratePairedPanelsImpl(std::string name1, std::string name2, H | |||
408 | } | 442 | } |
409 | } | 443 | } |
410 | 444 | ||
445 | if (options.max_len_diff >= 0) { | ||
446 | if (std::abs(static_cast<int>(question.getText().size() - solution.getText().size())) > options.max_len_diff) { | ||
447 | return false; | ||
448 | } | ||
449 | } | ||
450 | |||
411 | if (height == kMiddle && colour == kWhite) { | 451 | if (height == kMiddle && colour == kWhite) { |
412 | SavePanel(name1, question.getText(), question.getText(), options); | 452 | SavePanel(name1, question.getText(), question.getText(), options); |
413 | SavePanel(name2, solution.getText(), solution.getText(), options); | 453 | SavePanel(name2, solution.getText(), solution.getText(), options); |
@@ -587,7 +627,30 @@ void Generator::GenerateCrossTower( | |||
587 | std::string west_other_name3) | 627 | std::string west_other_name3) |
588 | { | 628 | { |
589 | std::vector<std::vector<std::string>> sets = cross_tower_->GetPuzzleSet(rng_); | 629 | std::vector<std::vector<std::string>> sets = cross_tower_->GetPuzzleSet(rng_); |
590 | 630 | ||
631 | SavePanel(north_tower_name, "", sets[0][0]); | ||
632 | SavePanel(north_lookout_name, "", sets[0][0]); | ||
633 | SavePanel(north_other_name1, sets[0][1], sets[0][1]); | ||
634 | SavePanel(north_other_name2, sets[0][2], sets[0][2]); | ||
635 | SavePanel(north_other_name3, sets[0][3], sets[0][3]); | ||
636 | |||
637 | SavePanel(south_tower_name, "", sets[1][0]); | ||
638 | SavePanel(south_lookout_name, "", sets[1][0]); | ||
639 | SavePanel(south_other_name1, sets[1][1], sets[1][1]); | ||
640 | SavePanel(south_other_name2, sets[1][2], sets[1][2]); | ||
641 | SavePanel(south_other_name3, sets[1][3], sets[1][3]); | ||
642 | |||
643 | SavePanel(east_tower_name, "", sets[2][0]); | ||
644 | SavePanel(east_lookout_name, "", sets[2][0]); | ||
645 | SavePanel(east_other_name1, sets[2][1], sets[2][1]); | ||
646 | SavePanel(east_other_name2, sets[2][2], sets[2][2]); | ||
647 | SavePanel(east_other_name3, sets[2][3], sets[2][3]); | ||
648 | |||
649 | SavePanel(west_tower_name, "", sets[3][0]); | ||
650 | SavePanel(west_lookout_name, "", sets[3][0]); | ||
651 | SavePanel(west_other_name1, sets[3][1], sets[3][1]); | ||
652 | SavePanel(west_other_name2, sets[3][2], sets[3][2]); | ||
653 | SavePanel(west_other_name3, sets[3][3], sets[3][3]); | ||
591 | } | 654 | } |
592 | 655 | ||
593 | void Generator::SavePanel(std::string name, std::string question, std::string answer, GenerateOptions options) { | 656 | void Generator::SavePanel(std::string name, std::string question, std::string answer, GenerateOptions options) { |
@@ -606,7 +669,7 @@ void Generator::SavePanel(std::string name, std::string question, std::string an | |||
606 | std::shuffle(indicies.begin(), indicies.end(), rng_); | 669 | std::shuffle(indicies.begin(), indicies.end(), rng_); |
607 | 670 | ||
608 | for (int i=0; i<numToObscure; i++) { | 671 | for (int i=0; i<numToObscure; i++) { |
609 | if (question[indicies[i]] != ' ') { | 672 | if (question[indicies[i]] != ' ' && question[indicies[i]] != '-') { |
610 | question[indicies[i]] = '?'; | 673 | question[indicies[i]] = '?'; |
611 | } | 674 | } |
612 | } | 675 | } |
@@ -653,6 +716,10 @@ verbly::filter Generator::GetWordFilter(FilterDirection dir, GenerateOptions opt | |||
653 | return wordFilter; | 716 | return wordFilter; |
654 | } | 717 | } |
655 | 718 | ||
719 | bool isDigitWrapper(unsigned char ch) { | ||
720 | return std::isdigit(ch); | ||
721 | } | ||
722 | |||
656 | bool Generator::IsClueTrivial(Height height, Colour colour, const verbly::form& clue, const verbly::form& solution) const | 723 | bool Generator::IsClueTrivial(Height height, Colour colour, const verbly::form& clue, const verbly::form& solution) const |
657 | { | 724 | { |
658 | if (height == kTop && colour == kWhite) | 725 | if (height == kTop && colour == kWhite) |
@@ -670,6 +737,41 @@ bool Generator::IsClueTrivial(Height height, Colour colour, const verbly::form& | |||
670 | return (clue.getId() == solution.getId()) | 737 | return (clue.getId() == solution.getId()) |
671 | || !database_->forms((verbly::filter)clue && (verbly::form::merographs %= solution)).all().empty() | 738 | || !database_->forms((verbly::filter)clue && (verbly::form::merographs %= solution)).all().empty() |
672 | || !database_->forms((verbly::filter)clue && (verbly::form::holographs %= solution)).all().empty(); | 739 | || !database_->forms((verbly::filter)clue && (verbly::form::holographs %= solution)).all().empty(); |
740 | } else if (height == kMiddle && colour == kRed) { | ||
741 | if (clue.getComplexity() == 2 && solution.getComplexity() == 1) { | ||
742 | auto words = hatkirby::split<std::vector<std::string>>(clue.getText(), " "); | ||
743 | for (const auto& word : words) { | ||
744 | if (word == solution.getText()) { | ||
745 | return true; | ||
746 | } | ||
747 | } | ||
748 | } | ||
749 | } else if (height == kMiddle && colour == kYellow) { | ||
750 | if (clue.getComplexity() == solution.getComplexity()) { | ||
751 | auto clueWords = hatkirby::split<std::vector<std::string>>(clue.getText(), " "); | ||
752 | auto solutionWords = hatkirby::split<std::vector<std::string>>(solution.getText(), " "); | ||
753 | std::sort(clueWords.begin(), clueWords.end()); | ||
754 | std::sort(solutionWords.begin(), solutionWords.end()); | ||
755 | if (clueWords == solutionWords) { | ||
756 | return true; | ||
757 | } | ||
758 | } | ||
759 | } else if (height == kTop && colour == kYellow) { | ||
760 | std::set<std::string> hint_stressless; | ||
761 | for (const verbly::pronunciation& pronunciation : clue.getPronunciations()) { | ||
762 | std::string stressed = hatkirby::implode(pronunciation.getPhonemes(), " "); | ||
763 | std::string stressless; | ||
764 | std::remove_copy_if(stressed.begin(), stressed.end(), std::back_inserter(stressless), &isDigitWrapper); | ||
765 | hint_stressless.insert(stressless); | ||
766 | } | ||
767 | for (const verbly::pronunciation& pronunciation : solution.getPronunciations()) { | ||
768 | std::string stressed = hatkirby::implode(pronunciation.getPhonemes(), " "); | ||
769 | std::string stressless; | ||
770 | std::remove_copy_if(stressed.begin(), stressed.end(), std::back_inserter(stressless), &isDigitWrapper); | ||
771 | if (hint_stressless.count(stressless)) { | ||
772 | return true; | ||
773 | } | ||
774 | } | ||
673 | } | 775 | } |
674 | return false; | 776 | return false; |
675 | } | 777 | } |