void Dictionary::printInteractiveSuggestions(Words const& active_words, unsigned const show) const { assert(!active_words.empty()); assert(show > 0); vector<pair<double, Word>> suggestions; suggestions.reserve(active_words.size()); puts("Suggestions:"); { DividedWordList dl(wordLength()); auto it = keys_.cbegin(); do { dl.build(*this, active_words, *it); suggestions.push_back(make_pair(-dl.entropy(), *it)); } while (++it != keys_.cend()); } sort(suggestions.begin(), suggestions.end()); { auto it = suggestions.cbegin(); unsigned i = 0; do { printf("\t%2u: ", ++i); describeWord(stdout, it->second); putchar('\n'); } while (++it != suggestions.cend() && i < show); } putchar('\n'); }
void DividedWordList::build(Dictionary const& dictionary, Words const& ws, Word const guess) { assert(size() == dictionary.wordLength() + 1); assert(!ws.empty()); for (auto it = begin(); it != end(); ++it) it->clear(); { auto it = ws.cbegin(); do { unsigned const c = correct_letters(*it, guess); assert(c <= dictionary.wordLength()); (*this)[c].update(dictionary, *it); } while (++it != ws.cend()); } }
void Dictionary::recursiveInteractive(Words const& active_words) const { if (active_words.empty()) { puts("No choices remain.\n"); } else if (active_words.size() == 1) { fputs("Only choice remaining is: ", stdout); describeWord(stdout, active_words.front()); puts("\n"); } else { printf("%u choices remain", static_cast<unsigned>(active_words.size())); if (active_words.size() <= 5) { // If we're near the end, print a list fputs(": ", stdout); auto it = active_words.cbegin(); describeWord(stdout, *it); do { fputs(", ", stdout); describeWord(stdout, *it); } while (++it != active_words.cend()); putchar('\n'); } else { puts("."); } putchar('\n'); string cmd; for (;;) { printInteractiveSuggestions(active_words); fputs("Give your move in form \"<word> <result>\" like \"TAXI 2\"\n\n" "> ", stdout); fflush(stdout); if (!read_uppercase_word(stdin, &cmd)) break; // EOF Word tried; unsigned matched; if (parse_move(cmd, wordLength(), &tried, &matched)) { DividedWordList dl(wordLength()); dl.build(*this, active_words, tried); recursiveInteractive(dl[matched].choices()); break; } puts("Invalid move.\n"); } } }
void Dictionary::recursiveSolve(AbstractSolutionAcceptor * const acceptor, Words const& active_words, AnswerSequence * const runningAnswersp) const { assert(!active_words.empty()); if (active_words.size() == 1) { // We found an end node acceptor->acceptSolution(*runningAnswersp, active_words.front()); } else { DividedWordList dl(wordLength()); double best_entropy = -1.0; Word best_word = 0; // Of all of the words in active_words, find the one that // will gives us the most even split (and thus the highest entropy) { auto it = keys_.cbegin(); do { dl.build(*this, active_words, *it); double const entropy = dl.entropy(); if (entropy > best_entropy) { best_entropy = entropy; best_word = *it; } } while (++it != keys_.cend()); } assert(popcount32(best_word) == wordLength()); // Now that we have found our best word, recompute the split dl.build(*this, active_words, best_word); assert(dl.entropy() == best_entropy); acceptor->acceptBranch(*runningAnswersp, best_word, best_entropy, dl); // Now recursively descend for (unsigned i = 0; i < wordLength() + 1; i++) { const WordsWithTotalChoices& wwtc = dl[i]; if (wwtc.count() > 0) { runningAnswersp->push_back(i); recursiveSolve(acceptor, wwtc.choices(), runningAnswersp); runningAnswersp->pop_back(); } } } }
LicensePos findLicense( const Words & words, const QList< Statement > & license, int & idx ) { LicensePos res{ -1, -1 }; int firstWord = 0; // Find first real word. for( int i = 0; i < license.count(); ++i ) { if( license.at( i ).type() == Statement::Word ) { firstWord = i; break; } } auto first = words.cbegin(); first += idx; auto it = std::find( first, words.cend(), WordWithPlace{ license.at( firstWord ), -1, -1 } ); while( it != words.cend() ) { idx = std::distance( words.cbegin(), it ); bool found = true; int wp = idx; for( int j = firstWord; j < license.count(); ++j ) { if( license.at( j ).type() == Statement::Word ) { // Skip line endings. while( wp < words.count() && words.at( wp ).m_st.type() == Statement::LineEnding ) { ++wp; } if( wp >= words.count() ) { found = false; break; } if( license.at( j ).word() != words.at( wp ).m_st.word() ) { found = false; break; } } else if( license.at( j ).type() == Statement::SkipLine ) { bool skipped = false; if( wp < words.count() && words.at( wp ).m_st.type() == Statement::LineEnding ) { ++wp; skipped = true; } while( wp < words.count() && words.at( wp ).m_st.type() != Statement::LineEnding ) { ++wp; skipped = true; } if( ( wp >= words.count() && j < license.count() - 1 ) || !skipped ) { found = false; break; } if( wp < words.count() - 1 && words.at( wp ).m_st.type() == Statement::LineEnding ) --wp; } else if( license.at( j ).type() == Statement::SkipWord ) { // Skip line endings. while( wp < words.count() && words.at( wp ).m_st.type() == Statement::LineEnding ) { ++wp; } } if( j < license.count() - 1 ) ++wp; } // If license was found. if( found ) { if( wp == words.count() ) --wp; int x = idx; if( firstWord > 0 ) { for( int i = firstWord - 1; i >= 0; --i ) { if( license.at( i ).type() == Statement::SkipWord ) { while( x > 0 && words.at( x - 1 ).m_st.type() != Statement::Word ) { --x; } --x; } else if( license.at( i ).type() == Statement::SkipLine ) { --x; while( x > 0 && words.at( x - 1 ).m_st.type() != Statement::LineEnding ) { --x; } } } if( x < 0 ) x = 0; } res = { ( license.at( 0 ).type() == Statement::SkipFirstSpaces || license.at( 0 ).type() == Statement::SkipLine ? words.at( x ).m_posWithSpaces : words.at( x ).m_pos ), words.at( wp ).m_pos + words.at( wp ).m_st.word().length() }; idx = wp + 1; break; } if( wp >= words.count() ) break; it = std::find( ++it, words.cend(), WordWithPlace{ license.at( firstWord ), -1, -1 } ); } return res; } // findLicense