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(); } } } }