ChiaraStellata

Wordle Solver (C++ optimized)

Jan 15th, 2022 (edited)
674
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 26.50 KB | None | 0 0
  1. #include <iostream>
  2. #include <fstream>
  3. #include <string>
  4. #include <vector>
  5. #include <map>
  6. #include <chrono>
  7. #include <cstdint>
  8.  
  9. using namespace std;
  10. using namespace std::chrono;
  11.  
  12. // From https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt
  13. // const char* ANSWER_WORD_FILE = "sgb-words.txt";
  14. // const char* GUESS_WORD_FILE = "sgb-words.txt";
  15. // From https://boardgames.stackexchange.com/a/38386/3054
  16. // const char* ANSWER_WORD_FILE = "Collins Scrabble Words (2019).txt";
  17. // const char* GUESS_WORD_FILE = "Collins Scrabble Words (2019).txt";
  18. // From http://www.mieliestronk.com/corncob_caps.txt
  19. // const char* ANSWER_WORD_FILE = "corncob_caps.txt";
  20. // const char* GUESS_WORD_FILE = "corncob_caps.txt";
  21. // From https://github.com/first20hours/google-10000-english/blob/master/google-10000-english.txt
  22. // const char* ANSWER_WORD_FILE = "google-10000-english.txt";
  23. // const char* GUESS_WORD_FILE = "google-10000-english.txt";
  24. // From https://github.com/dwyl/english-words
  25. // const char* ANSWER_WORD_FILE = "words.txt";
  26. // const char* GUESS_WORD_FILE = "words.txt";
  27. // From https://github.com/Kinkelin/WordleCompetition/blob/main/data/official/combined_wordlist.txt
  28. // const char* ANSWER_WORD_FILE = "combined_wordlist.txt";
  29. const char* GUESS_WORD_FILE = "combined_wordlist.txt";
  30. // From https://github.com/Kinkelin/WordleCompetition/blob/main/data/official/shuffled_real_wordles.txt
  31. const char* ANSWER_WORD_FILE = "shuffled_real_wordles.txt";
  32. // const char* GUESS_WORD_FILE = "shuffled_real_wordles.txt";
  33.  
  34. int word_length;
  35. vector<char*> guess_words;
  36. vector<char*> answer_words;
  37. char* best_first_guess = NULL;
  38.  
  39. bool all_alpha(string s) {
  40.     for (unsigned int i = 0; i < s.length(); i++) {
  41.         if (!isalpha(s[i])) {
  42.             return false;
  43.         }
  44.     }
  45.     return true;
  46. }
  47.  
  48. void toupper_cstr(char* s) {
  49.     while (*s != '\0') {
  50.         *s = toupper(*s);
  51.         s++;
  52.     }
  53. }
  54.  
  55. vector<char*> read_word_file(const char* filename) {
  56.     vector<char*> result;
  57.     string line;
  58.     ifstream infile(filename);
  59.     int max_length = 0;
  60.     while (getline(infile, line)) {
  61.         if (all_alpha(line)) {
  62.             if (line.length() == word_length) {
  63.                 char* word = new char[word_length + 1];
  64.                 strcpy(word, line.c_str());
  65.                 toupper_cstr(word);
  66.                 result.push_back(word);
  67.             }
  68.         }
  69.     }
  70.     return result;
  71. }
  72.  
  73. // Hints are packed into unsigned integers, one ternary digit per bit, least-significant digit is last character
  74. // This is more space-efficient than using two bits per hint, which is important for cache efficiency.
  75. // typedef uint64_t hint; // Use for number of words >= 21
  76. typedef uint32_t hint; // Use for number of words <= 20
  77. hint num_hint_values; // max hint value plus one
  78.  
  79. hint get_wordle_hints(const char* guess, const char* answer) {
  80.     hint result = 0;
  81.     char buf[80];
  82.     strcpy(buf, answer);
  83.  
  84.     for (int i = 0; i < word_length; i++) {
  85.         if (guess[i] == answer[i]) {
  86.             buf[i] = ' ';
  87.         }
  88.     }
  89.     for (int i = 0; i < word_length; i++) {
  90.         result *= 3;
  91.         if (guess[i] == answer[i]) {
  92.             result += 2; // green
  93.         } else {
  94.             char* char_pos = strchr(buf, guess[i]);
  95.             if (char_pos != NULL) {
  96.                 result += 1; // yellow
  97.                 *char_pos = ' ';
  98.             }
  99.             // Otherwise black which is zero, do nothing
  100.         }
  101.     }
  102.     return result;
  103. }
  104.  
  105. hint string_to_hint(string hint_str) {
  106.     hint result = 0;
  107.     for (int i = 0; i < hint_str.length(); i++) {
  108.         result *= 3;
  109.         if (hint_str[i] == 'g') {
  110.             result += 2;
  111.         } else if (hint_str[i] == 'y') {
  112.             result += 1;
  113.         }
  114.     }
  115.     return result;
  116. }
  117.  
  118. string hint_to_string(hint hint) {
  119.     string result;
  120.     for (int i = 0; i < word_length; i++) {
  121.         char c;
  122.         if ((hint % 3) == 2) {
  123.             c = 'g';
  124.         } else if ((hint % 3) == 1) {
  125.             c = 'y';
  126.         } else {
  127.             c = 'k';
  128.         }
  129.         result = c + result;
  130.         hint /= 3;
  131.     }
  132.     return result;
  133. }
  134.  
  135. struct guess {
  136.     guess(char* word, hint hint) {
  137.         this->word = word;
  138.         this->hint = hint;
  139.     }
  140.  
  141.     char* word;
  142.     hint hint;
  143. };
  144.  
  145. bool matches_hints(const char* answer, const vector<guess>& guesses_so_far) {
  146.     for (int i = 0; i < guesses_so_far.size(); i++) {
  147.         if (get_wordle_hints(guesses_so_far[i].word, answer) != guesses_so_far[i].hint) {
  148.             return false;
  149.         }
  150.     }
  151.     return true;
  152. }
  153.  
  154. vector<char*> get_valid_words(const vector<char*>& answer_words, const vector<guess>& guesses_so_far) {
  155.     vector<char*> result;
  156.     for (int i = 0; i < answer_words.size(); i++) {
  157.         if (matches_hints(answer_words[i], guesses_so_far)) {
  158.             result.push_back(answer_words[i]);
  159.         }
  160.     }
  161.     return result;
  162. }
  163.  
  164. inline bool should_prefer_dense(int num_words) {
  165.      return (num_hint_values / num_words) < 250;  // Experimentally determined constant
  166. }
  167.  
  168. bool is_among_guesses(char* word, const vector<guess>& guesses_so_far) {
  169.     for (int i = 0; i < guesses_so_far.size(); i++) {
  170.         if (strcmp(word, guesses_so_far[i].word) == 0) {
  171.             return true;
  172.         }
  173.     }
  174.     return false;
  175. }
  176.  
  177. map<char*, bool> forbidden_first_guesses;
  178.  
  179. void forbid_first_guess(char* word) {
  180.     forbidden_first_guesses[word] = true;
  181.     best_first_guess = NULL;
  182. }
  183.  
  184. int* hint_class_counts = NULL;
  185. char* get_best_guess(const vector<char*>& valid_words, bool& result_is_correct_word, const vector<guess>& guesses_so_far, int& rating) {
  186.     if (valid_words.size() == 0) {
  187.         rating = 0;
  188.         return NULL;
  189.     } else if (valid_words.size() == 1) {
  190.         result_is_correct_word = true;
  191.         rating = 0;
  192.         return valid_words[0];
  193.     }
  194.  
  195.     map<string, bool> valid_words_map;
  196.     for (int i = 0; i < valid_words.size(); i++) {
  197.         valid_words_map[valid_words[i]] = true;
  198.     }
  199.  
  200.     char* best_guess = NULL;
  201.     int best_guess_rating = INT_MAX;
  202.     bool best_matches_hints = false;
  203.  
  204.     if (should_prefer_dense(valid_words.size())) {
  205.         // Dense case - use direct array indexing
  206.         for (int i = 0; i < guess_words.size(); i++) {
  207.             if (guess_words[i][0] == '\0') {
  208.                 continue; // Word was removed from dictionary during guessing
  209.             }
  210.             memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  211.             bool skip_to_next_word = false;
  212.             for (int j = 0; j < valid_words.size(); j++) {
  213.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  214.                 hint_class_counts[idx]++;
  215.                 if (hint_class_counts[idx] > best_guess_rating) {
  216.                     skip_to_next_word = true;
  217.                     break;
  218.                 }
  219.             }
  220.             if (skip_to_next_word) {
  221.                 continue;
  222.             }
  223.  
  224.             int guess_rating = 0;
  225.             // Rating for this guess is max of all hint class counts
  226.             for (int j = 0; j < num_hint_values; j++) {
  227.                 if (hint_class_counts[j] >= guess_rating) {
  228.                     guess_rating = hint_class_counts[j];
  229.                 }
  230.             }
  231.             if (guess_rating < best_guess_rating && !is_among_guesses(guess_words[i], guesses_so_far) && !(guesses_so_far.size() == 0 && forbidden_first_guesses.find(guess_words[i]) != forbidden_first_guesses.end())) {
  232.                 best_guess = guess_words[i];
  233.                 best_guess_rating = guess_rating;
  234.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  235.             } else if (!best_matches_hints && guess_rating == best_guess_rating && valid_words_map.find(guess_words[i]) != valid_words_map.end() && !is_among_guesses(guess_words[i], guesses_so_far) && !(guesses_so_far.size() == 0 && forbidden_first_guesses.find(guess_words[i]) != forbidden_first_guesses.end())) {
  236.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  237.                 // so far, so that we have a chance to guess the word.
  238.                 best_guess = guess_words[i];
  239.                 best_guess_rating = guess_rating;
  240.                 best_matches_hints = true;
  241.             }
  242.         }
  243.     } else {
  244.         // Sparse case - use map
  245.         map<hint, int> hint_class_counts;
  246.         for (int i = 0; i < guess_words.size(); i++) {
  247.             hint_class_counts.clear();
  248.             bool skip_to_next_word = false;
  249.             for (int j = 0; j < valid_words.size(); j++) {
  250.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  251.                 hint_class_counts[idx]++;
  252.                 if (hint_class_counts[idx] > best_guess_rating) {
  253.                     skip_to_next_word = true;
  254.                     break;
  255.                 }
  256.             }
  257.             if (skip_to_next_word) {
  258.                 continue;
  259.             }
  260.  
  261.             int guess_rating = 0;
  262.             // Rating for this guess is max of all hint class counts
  263.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  264.                 if (iter->second >= guess_rating) {
  265.                     guess_rating = iter->second;
  266.                 }
  267.             }
  268.  
  269.             if (guess_rating < best_guess_rating && !is_among_guesses(guess_words[i], guesses_so_far) && !(guesses_so_far.size() == 0 && forbidden_first_guesses.find(guess_words[i]) != forbidden_first_guesses.end())) {
  270.                 best_guess = guess_words[i];
  271.                 best_guess_rating = guess_rating;
  272.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  273.             } else if (!best_matches_hints && guess_rating == best_guess_rating && valid_words_map.find(guess_words[i]) != valid_words_map.end() && !is_among_guesses(guess_words[i], guesses_so_far) && !(guesses_so_far.size() == 0 && forbidden_first_guesses.find(guess_words[i]) != forbidden_first_guesses.end())) {
  274.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  275.                 // so far, so that we have a chance to guess the word.
  276.                 best_guess = guess_words[i];
  277.                 best_guess_rating = guess_rating;
  278.                 best_matches_hints = true;
  279.             }
  280.         }
  281.     }
  282.  
  283.     result_is_correct_word = false;
  284.     rating = best_guess_rating;
  285.     return best_guess;
  286. }
  287.  
  288. bool can_guess_word_max_depth(const vector<char*>& valid_words, char* word, const vector<guess>& guesses_so_far, int max_depth) {
  289.     if (valid_words.size() <= 1) {
  290.         return true;
  291.     }
  292.     if (max_depth == 0) {
  293.         return false;
  294.     }
  295.  
  296.     for (int i = 0; i < guess_words.size(); i++) {
  297.         if (is_among_guesses(guess_words[i], guesses_so_far)) {
  298.             continue;
  299.         }
  300.         vector<guess> guesses_so_far2 = guesses_so_far;
  301.         guesses_so_far2.push_back(guess(guess_words[i], get_wordle_hints(guess_words[i], word)));
  302.         vector<char*> valid_words2 = get_valid_words(valid_words, guesses_so_far2);
  303.         if (valid_words2.size() == valid_words.size()) {
  304.             continue; // No new information
  305.         }
  306.  
  307.         if (!can_guess_word_max_depth(valid_words2, word, guesses_so_far2, max_depth - 1)) {
  308.             return false;
  309.         }
  310.     }
  311.  
  312.     return true;
  313. }
  314.  
  315. void cache_best_first_guess() {
  316.     if (best_first_guess == NULL) {
  317.         vector<guess> guesses_so_far;
  318.         bool result_is_correct_word;
  319.         int rating;
  320.         cout << "Computing and caching best first guess..." << endl;
  321.         auto start = high_resolution_clock::now();
  322.         best_first_guess = get_best_guess(answer_words, result_is_correct_word, guesses_so_far, rating);
  323.         auto stop = high_resolution_clock::now();
  324.         cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  325.         // best_first_guess = _strdup("RAILE");
  326.     }
  327. }
  328.  
  329. void find_best_first_guess() {
  330.     cache_best_first_guess();
  331.     cout << "Best first guess: " << best_first_guess << endl;
  332. }
  333.  
  334. void print_words(const vector<char*>& valid_words) {
  335.     cout << "[";
  336.     int limit = valid_words.size() <= 50 ? valid_words.size() : 20;
  337.     for (int i = 0; i < limit; i++) {
  338.         cout << valid_words[i] << (i < valid_words.size() - 1 ? ", " : "");
  339.     }
  340.     if (limit < valid_words.size()) {
  341.         cout << "(" << (valid_words.size() - limit) << " other words" << ")";
  342.     }
  343.     cout << "]";
  344. }
  345.  
  346. void guess_unknown_word() {
  347.     vector<guess> guesses_so_far;
  348.     bool result_is_correct_word;
  349.     int rating;
  350.  
  351.     string hint_string;
  352.     char* current_guess;
  353.     do {
  354.         cache_best_first_guess();
  355.         current_guess = best_first_guess;
  356.         cout << "Next guess: " << current_guess << endl;
  357.         cout << "Enter result (k for black, y for yellow, g for green, or ENTER if word is not accepted): ";
  358.         getline(cin, hint_string);
  359.         if (hint_string.length() == 0) {
  360.             strcpy(best_first_guess, ""); // Remove from dictionary
  361.             best_first_guess = NULL;
  362.         }
  363.     } while (hint_string.length() == 0);
  364.     guesses_so_far.push_back(guess(current_guess, string_to_hint(hint_string)));
  365.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  366.     print_words(valid_words);
  367.     cout << endl << endl;
  368.  
  369.     while (true) {
  370.         char* current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  371.         if (current_guess == NULL) {
  372.             cout << "Word is not in dictionary." << endl;
  373.             break;
  374.         } else if (result_is_correct_word) {
  375.             cout << "The word is: " << current_guess << endl;
  376.             break;
  377.         }
  378.         cout << "Next guess: " << current_guess << endl;
  379.         cout << "Enter result (k for black, y for yellow, g for green, or ENTER if word is not accepted): ";
  380.         getline(cin, hint_string);
  381.         if (hint_string.length() == 0) {
  382.             strcpy(current_guess, ""); // Remove from dictionary
  383.             continue;
  384.         }
  385.         guesses_so_far.push_back(guess(current_guess, string_to_hint(hint_string)));
  386.         valid_words = get_valid_words(valid_words, guesses_so_far);
  387.         print_words(valid_words);
  388.         cout << endl << endl;
  389.     }
  390. }
  391.  
  392. void solve_single_word_helper(char* answer) {
  393.     vector<guess> guesses_so_far;
  394.     bool result_is_correct_word;
  395.     int rating;
  396.  
  397.     auto start = high_resolution_clock::now();
  398.     cache_best_first_guess();
  399.     char* current_guess = best_first_guess;
  400.     hint hint = get_wordle_hints(current_guess, answer);
  401.     guesses_so_far.push_back(guess(current_guess, hint));
  402.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  403.     cout << current_guess << " " << hint_to_string(hint) << " ";
  404.     print_words(valid_words);
  405.     cout << endl << endl;
  406.  
  407.     while (strcmp(current_guess, answer) != 0) {
  408.         current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  409.         if (current_guess == NULL) {
  410.             cout << "Word is not in dictionary. You may have typed it incorrectly." << endl;
  411.             break;
  412.         }
  413.         hint = get_wordle_hints(current_guess, answer);
  414.         guesses_so_far.push_back(guess(current_guess, hint));
  415.         valid_words = get_valid_words(valid_words, guesses_so_far);
  416.         cout << current_guess << " " << hint_to_string(hint) << " ";
  417.         print_words(valid_words);
  418.         cout << endl << endl;
  419.     }
  420.     auto stop = high_resolution_clock::now();
  421.     cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  422.     cout << "Number of guesses: " << guesses_so_far.size() << endl;
  423. }
  424.  
  425. void solve_single_word() {
  426.     char* answer = NULL;
  427.     do {
  428.         cout << "Enter solution word: ";
  429.         string answer_str;
  430.         getline(cin, answer_str);
  431.         answer = _strdup(answer_str.c_str());
  432.         toupper_cstr(answer);
  433.         if (strlen(answer) != word_length) {
  434.             cout << "Word is wrong length, must be " << word_length << " letters." << endl;
  435.             continue;
  436.         }
  437.         break;
  438.     } while (true);
  439.  
  440.     solve_single_word_helper(answer);
  441. }
  442.  
  443. void solve_all_words_in_dictionary() {
  444.     vector<guess> guesses_so_far;
  445.     bool result_is_correct_word;
  446.     int rating;
  447.  
  448.     auto overall_start = high_resolution_clock::now();
  449.  
  450.     double best_average = 10000.0f;
  451.     char* best_first_word_solve_all = NULL;
  452.  
  453.     while (true) {
  454.         cout << "Precomputing best first guess..." << endl;
  455.         cache_best_first_guess();
  456.         char* first_guess = best_first_guess;
  457.         cout << "First guess: " << first_guess << endl;
  458.  
  459.         char** second_guesses = NULL;
  460.         if (answer_words.size() * 1000 > num_hint_values) {
  461.             cout << "Precomputing best second guesses..." << endl;
  462.             auto start = high_resolution_clock::now();
  463.             second_guesses = new char* [num_hint_values];
  464.             for (int i = 0; i < num_hint_values; i++) {
  465.                 vector<guess> guesses_so_far;
  466.                 guesses_so_far.push_back(guess(first_guess, i));
  467.                 vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  468.                 second_guesses[i] = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  469.  
  470.                 // if ((i % 1000) == 0) {
  471.                 //     auto stop = high_resolution_clock::now();
  472.                 //     cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * num_hint_values << " s" << endl;
  473.                 // }
  474.             }
  475.             auto stop = high_resolution_clock::now();
  476.             cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  477.         }
  478.  
  479.         char*** third_guesses = NULL;
  480.         if (sqrt(answer_words.size() * 1000) > num_hint_values) {
  481.             cout << "Precomputing third guesses..." << endl;
  482.             auto start = high_resolution_clock::now();
  483.             third_guesses = new char** [num_hint_values * num_hint_values];
  484.             for (int i = 0; i < num_hint_values; i++) {
  485.                 vector<guess> guesses_so_far_first;
  486.                 guesses_so_far_first.push_back(guess(first_guess, i));
  487.                 vector<char*> valid_words_first = get_valid_words(answer_words, guesses_so_far_first);
  488.  
  489.                 third_guesses[i] = new char* [num_hint_values];
  490.                 for (int j = 0; j < num_hint_values; j++) {
  491.                     vector<guess> guesses_so_far;
  492.                     guesses_so_far.push_back(guess(first_guess, i));
  493.                     guesses_so_far.push_back(guess(second_guesses[i], j));
  494.                     vector<char*> valid_words = get_valid_words(valid_words_first, guesses_so_far);
  495.                     third_guesses[i][j] = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  496.                 }
  497.                 // if ((i % 1000) == 0) {
  498.                 //     auto stop = high_resolution_clock::now();
  499.                 //     cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * num_hint_values << " s" << endl;
  500.                 // }
  501.             }
  502.             auto stop = high_resolution_clock::now();
  503.             cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  504.         }
  505.  
  506.         int max_guesses = 0;
  507.         char* worst_word = NULL;
  508.  
  509.         cout << "Solving all words in dictionary..." << endl;
  510.         auto start = high_resolution_clock::now();
  511.         int histogram[50] = { 0 };
  512.         for (int i = 0; i < answer_words.size(); i++) {
  513.             if ((i % 1000) == 0) {
  514.                 cout << i << endl;
  515.                 // auto stop = high_resolution_clock::now();
  516.                 // cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * answer_words.size() << " s" << endl;
  517.             }
  518.             vector<guess> guesses_so_far;
  519.             char* current_guess = first_guess;
  520.  
  521.             guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  522.  
  523.             int number_of_guesses = 1;
  524.             hint first_hint;
  525.             vector<char*> valid_words;
  526.  
  527.             if (strcmp(current_guess, answer_words[i]) != 0) {
  528.                 first_hint = get_wordle_hints(current_guess, answer_words[i]);
  529.                 if (second_guesses != NULL) {
  530.                     current_guess = second_guesses[first_hint];
  531.                 } else {
  532.                     current_guess = get_best_guess(answer_words, result_is_correct_word, guesses_so_far, rating);
  533.                 }
  534.                 number_of_guesses++;
  535.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  536.                 valid_words = get_valid_words(answer_words, guesses_so_far);
  537.             }
  538.  
  539.             if (third_guesses != NULL && strcmp(current_guess, answer_words[i]) != 0) {
  540.                 current_guess = third_guesses[first_hint][get_wordle_hints(current_guess, answer_words[i])];
  541.                 number_of_guesses++;
  542.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  543.                 valid_words = get_valid_words(valid_words, guesses_so_far);
  544.             }
  545.  
  546.             while (strcmp(current_guess, answer_words[i]) != 0) {
  547.                 current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  548.                 number_of_guesses++;
  549.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  550.                 valid_words = get_valid_words(valid_words, guesses_so_far);
  551.             }
  552.  
  553.             histogram[number_of_guesses]++;
  554.             if (number_of_guesses > max_guesses) {
  555.                 worst_word = answer_words[i];
  556.                 max_guesses = number_of_guesses;
  557.             }
  558.         }
  559.         auto stop = high_resolution_clock::now();
  560.         cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  561.  
  562.         auto overall_stop = high_resolution_clock::now();
  563.         cout << "Total time: " << duration_cast<microseconds>(overall_stop - overall_start).count() / 1000000.0 << " s" << endl;
  564.         cout << "Time per word: " << duration_cast<microseconds>(overall_stop - overall_start).count() / 1000.0 / answer_words.size() << " ms" << endl;
  565.  
  566.         int total_count = 0, total_guesses = 0;
  567.         for (int i = 0; i < sizeof(histogram) / sizeof(histogram[0]); i++) {
  568.             if (histogram[i] != 0) {
  569.                 cout << i << ": " << histogram[i] << endl;
  570.                 total_count += histogram[i];
  571.                 total_guesses += i * histogram[i];
  572.             }
  573.         }
  574.         double average = double(total_guesses) / total_count;
  575.         cout << "Average: " << average << " guesses" << endl;
  576.         cout << "Worst word was " << worst_word << ":" << endl;
  577.         // solve_single_word_helper(worst_word);
  578.  
  579.         if (average < best_average) {
  580.             best_first_word_solve_all = first_guess;
  581.             best_average = average;
  582.         }
  583.         cout << "Best first word so far: " << best_first_word_solve_all << " with average of " << best_average << endl;
  584.  
  585.         // forbid_first_guess(first_guess);
  586.         break;
  587.     }
  588. }
  589.  
  590. int main() {
  591. #if 0
  592.     for (word_length = 1; word_length <= 500; word_length++) {
  593.         num_hint_values = 1;
  594.         for (int i = 0; i < word_length; i++) {
  595.             num_hint_values *= 3;
  596.         }
  597.  
  598.         guess_words = read_word_file(GUESS_WORD_FILE);
  599.         if (guess_words.size() > 0) {
  600.             cout << "Valid guesses : " << guess_words.size() << " words of length " << word_length << " in " << GUESS_WORD_FILE << endl;
  601.         }
  602.         if (strcmp(GUESS_WORD_FILE, ANSWER_WORD_FILE) == 0) {
  603.             answer_words = guess_words;
  604.         } else {
  605.             answer_words = read_word_file(ANSWER_WORD_FILE);
  606.         }
  607.         if (answer_words.size() > 0) {
  608.             cout << "Valid answers : " << answer_words.size() << " words of length " << word_length << " in " << ANSWER_WORD_FILE << endl;
  609.         }
  610.         if (answer_words.size() == 0 || guess_words.size() == 0) {
  611.             continue;
  612.         }
  613.  
  614.         if (should_prefer_dense(answer_words.size())) {
  615.             hint_class_counts = new int[num_hint_values];
  616.         }
  617.  
  618.         best_first_guess = NULL;
  619.         find_best_first_guess();
  620.  
  621.         delete[] hint_class_counts;
  622.         hint_class_counts = NULL;
  623.     }
  624.     return 0;
  625. #endif
  626.  
  627.     string newline;
  628.     cout << "Enter word length: ";
  629.     cin >> word_length;
  630.     getline(cin, newline);
  631.     num_hint_values = 1;
  632.     for (int i = 0; i < word_length; i++) {
  633.         num_hint_values *= 3;
  634.     }
  635.  
  636.     guess_words = read_word_file(GUESS_WORD_FILE);
  637.     cout << "Valid guesses : " << guess_words.size() << " words of length " << word_length << " in " << GUESS_WORD_FILE << endl;
  638.     if (strcmp(GUESS_WORD_FILE, ANSWER_WORD_FILE) == 0) {
  639.         answer_words = guess_words;
  640.     } else {
  641.         answer_words = read_word_file(ANSWER_WORD_FILE);
  642.     }
  643.     cout << "Valid answers : " << answer_words.size() << " words of length " << word_length << " in " << ANSWER_WORD_FILE << endl;
  644.  
  645.     if (answer_words.size() == 0 || guess_words.size() == 0) {
  646.         cout << "No words of length " << word_length << " in wordlists, exiting." << endl;
  647.         return 1;
  648.     }
  649.  
  650.     if (should_prefer_dense(answer_words.size())) {
  651.         hint_class_counts = new int[num_hint_values];
  652.     }
  653.  
  654.     while (true) {
  655.         cout << "Select one:" << endl;
  656.         cout << " 1. Find best first guess" << endl;
  657.         cout << " 2. Guess unknown word (solve puzzle)" << endl;
  658.         cout << " 3. Show solution for a single given word" << endl;
  659.         cout << " 4. Solve all words in dictionary and show statistics" << endl;
  660.         cout << " 5. Quit" << endl;
  661.         cout << "Enter your choice: ";
  662.         int choice;
  663.         cin >> choice;
  664.         getline(cin, newline);
  665.  
  666.         switch (choice) {
  667.         case 1:
  668.             find_best_first_guess();
  669.             break;
  670.         case 2:
  671.             guess_unknown_word();
  672.             break;
  673.         case 3:
  674.             solve_single_word();
  675.             break;
  676.         case 4:
  677.             solve_all_words_in_dictionary();
  678.             break;
  679.         case 5:
  680.             return 0;
  681.         default:
  682.             cout << "Invalid option selected." << endl;
  683.             break;
  684.         }
  685.     }
  686. }
  687.  
Add Comment
Please, Sign In to add comment