ChiaraStellata

Wordle triple search (C++)

Jan 19th, 2022
269
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 43.85 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. // const char* GUESS_WORD_FILE = "combined_wordlist_rearranged.txt";
  31. // From https://github.com/Kinkelin/WordleCompetition/blob/main/data/official/shuffled_real_wordles.txt
  32. const char* ANSWER_WORD_FILE = "shuffled_real_wordles.txt";
  33. const char* GUESS_WORD_FILE = "shuffled_real_wordles.txt";
  34.  
  35. int word_length;
  36. vector<char*> guess_words;
  37. vector<char*> answer_words;
  38. char* best_first_guess = NULL;
  39.  
  40. bool all_alpha(string s) {
  41.     for (unsigned int i = 0; i < s.length(); i++) {
  42.         if (!isalpha(s[i])) {
  43.             return false;
  44.         }
  45.     }
  46.     return true;
  47. }
  48.  
  49. void toupper_cstr(char* s) {
  50.     while (*s != '\0') {
  51.         *s = toupper(*s);
  52.         s++;
  53.     }
  54. }
  55.  
  56. vector<char*> read_word_file(const char* filename) {
  57.     vector<char*> result;
  58.     string line;
  59.     ifstream infile(filename);
  60.     int max_length = 0;
  61.     while (getline(infile, line)) {
  62.         if (all_alpha(line)) {
  63.             if (line.length() == word_length) {
  64.                 char* word = new char[word_length + 1];
  65.                 strcpy(word, line.c_str());
  66.                 toupper_cstr(word);
  67.                 result.push_back(word);
  68.             }
  69.         }
  70.     }
  71.     return result;
  72. }
  73.  
  74. // Hints are packed into unsigned integers, one ternary digit per bit, least-significant digit is last character
  75. // This is more space-efficient than using two bits per hint, which is important for cache efficiency.
  76. // typedef uint64_t hint; // Use for number of words >= 21
  77. typedef uint32_t hint; // Use for number of words <= 20
  78. hint num_hint_values; // max hint value plus one
  79.  
  80. hint get_wordle_hints(const char* guess, const char* answer) {
  81.     hint result = 0;
  82.     char buf[80];
  83.     strcpy(buf, answer);
  84.  
  85.     for (int i = 0; i < word_length; i++) {
  86.         if (guess[i] == answer[i]) {
  87.             buf[i] = ' ';
  88.         }
  89.     }
  90.     for (int i = 0; i < word_length; i++) {
  91.         result *= 3;
  92.         if (guess[i] == answer[i]) {
  93.             result += 2; // green
  94.         } else {
  95.             char* char_pos = strchr(buf, guess[i]);
  96.             if (char_pos != NULL) {
  97.                 result += 1; // yellow
  98.                 *char_pos = ' ';
  99.             }
  100.             // Otherwise black which is zero, do nothing
  101.         }
  102.     }
  103.     return result;
  104. }
  105.  
  106. hint string_to_hint(string hint_str) {
  107.     hint result = 0;
  108.     for (int i = 0; i < hint_str.length(); i++) {
  109.         result *= 3;
  110.         if (hint_str[i] == 'g') {
  111.             result += 2;
  112.         } else if (hint_str[i] == 'y') {
  113.             result += 1;
  114.         }
  115.     }
  116.     return result;
  117. }
  118.  
  119. string hint_to_string(hint hint) {
  120.     string result;
  121.     for (int i = 0; i < word_length; i++) {
  122.         char c;
  123.         if ((hint % 3) == 2) {
  124.             c = 'g';
  125.         } else if ((hint % 3) == 1) {
  126.             c = 'y';
  127.         } else {
  128.             c = 'k';
  129.         }
  130.         result = c + result;
  131.         hint /= 3;
  132.     }
  133.     return result;
  134. }
  135.  
  136. struct guess {
  137.     guess(char* word, hint hint) {
  138.         this->word = word;
  139.         this->hint = hint;
  140.     }
  141.  
  142.     char* word;
  143.     hint hint;
  144. };
  145.  
  146. bool matches_hints(const char* answer, const vector<guess>& guesses_so_far) {
  147.     for (int i = 0; i < guesses_so_far.size(); i++) {
  148.         if (get_wordle_hints(guesses_so_far[i].word, answer) != guesses_so_far[i].hint) {
  149.             return false;
  150.         }
  151.     }
  152.     return true;
  153. }
  154.  
  155. int count_valid_words(const vector<char*>& answer_words, const vector<guess>& guesses_so_far) {
  156.     int count = 0;
  157.     for (int i = 0; i < answer_words.size(); i++) {
  158.         if (matches_hints(answer_words[i], guesses_so_far)) {
  159.             count++;
  160.         }
  161.     }
  162.     return count;
  163. }
  164.  
  165. vector<char*> get_valid_words(const vector<char*>& answer_words, const vector<guess>& guesses_so_far) {
  166.     vector<char*> result;
  167.     for (int i = 0; i < answer_words.size(); i++) {
  168.         if (matches_hints(answer_words[i], guesses_so_far)) {
  169.             result.push_back(answer_words[i]);
  170.         }
  171.     }
  172.     return result;
  173. }
  174.  
  175. inline bool should_prefer_dense(int num_words) {
  176.      return (num_hint_values / num_words) < 250;  // Experimentally determined constant
  177. }
  178.  
  179. bool is_among_guesses(char* word, const vector<guess>& guesses_so_far) {
  180.     for (int i = 0; i < guesses_so_far.size(); i++) {
  181.         if (strcmp(word, guesses_so_far[i].word) == 0) {
  182.             return true;
  183.         }
  184.     }
  185.     return false;
  186. }
  187.  
  188. map<char*, bool> forbidden_first_guesses;
  189.  
  190. void forbid_first_guess(char* word) {
  191.     forbidden_first_guesses[word] = true;
  192.     best_first_guess = NULL;
  193. }
  194.  
  195. int* hint_class_counts = NULL;
  196. char* get_best_guess(const vector<char*>& valid_words, bool& result_is_correct_word, const vector<guess>& guesses_so_far, int& rating) {
  197.     if (valid_words.size() == 0) {
  198.         rating = 0;
  199.         return NULL;
  200.     } else if (valid_words.size() == 1) {
  201.         result_is_correct_word = true;
  202.         rating = 0;
  203.         return valid_words[0];
  204.     }
  205.  
  206.     map<string, bool> valid_words_map;
  207.     for (int i = 0; i < valid_words.size(); i++) {
  208.         valid_words_map[valid_words[i]] = true;
  209.     }
  210.  
  211.     char* best_guess = NULL;
  212.     int best_guess_rating = INT_MAX;
  213.     bool best_matches_hints = false;
  214.  
  215.     if (should_prefer_dense(valid_words.size())) {
  216.         // Dense case - use direct array indexing
  217.         for (int i = 0; i < guess_words.size(); i++) {
  218.             if (guess_words[i][0] == '\0') {
  219.                 continue; // Word was removed from dictionary during guessing
  220.             }
  221.             memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  222.             bool skip_to_next_word = false;
  223.             for (int j = 0; j < valid_words.size(); j++) {
  224.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  225.                 hint_class_counts[idx]++;
  226.                 if (hint_class_counts[idx] > best_guess_rating) {
  227.                     skip_to_next_word = true;
  228.                     break;
  229.                 }
  230.             }
  231.             if (skip_to_next_word) {
  232.                 continue;
  233.             }
  234.  
  235.             int guess_rating = 0;
  236.             // Rating for this guess is max of all hint class counts
  237.             for (int j = 0; j < num_hint_values; j++) {
  238.                 if (hint_class_counts[j] >= guess_rating) {
  239.                     guess_rating = hint_class_counts[j];
  240.                 }
  241.             }
  242.             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())) {
  243.                 best_guess = guess_words[i];
  244.                 best_guess_rating = guess_rating;
  245.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  246.             } 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())) {
  247.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  248.                 // so far, so that we have a chance to guess the word.
  249.                 best_guess = guess_words[i];
  250.                 best_guess_rating = guess_rating;
  251.                 best_matches_hints = true;
  252.             }
  253.         }
  254.     } else {
  255.         // Sparse case - use map
  256.         map<hint, int> hint_class_counts;
  257.         for (int i = 0; i < guess_words.size(); i++) {
  258.             hint_class_counts.clear();
  259.             bool skip_to_next_word = false;
  260.             for (int j = 0; j < valid_words.size(); j++) {
  261.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  262.                 hint_class_counts[idx]++;
  263.                 if (hint_class_counts[idx] > best_guess_rating) {
  264.                     skip_to_next_word = true;
  265.                     break;
  266.                 }
  267.             }
  268.             if (skip_to_next_word) {
  269.                 continue;
  270.             }
  271.  
  272.             int guess_rating = 0;
  273.             // Rating for this guess is max of all hint class counts
  274.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  275.                 if (iter->second >= guess_rating) {
  276.                     guess_rating = iter->second;
  277.                 }
  278.             }
  279.  
  280.             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())) {
  281.                 best_guess = guess_words[i];
  282.                 best_guess_rating = guess_rating;
  283.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  284.             } 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())) {
  285.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  286.                 // so far, so that we have a chance to guess the word.
  287.                 best_guess = guess_words[i];
  288.                 best_guess_rating = guess_rating;
  289.                 best_matches_hints = true;
  290.             }
  291.         }
  292.     }
  293.  
  294.     result_is_correct_word = false;
  295.     rating = best_guess_rating;
  296.     return best_guess;
  297. }
  298.  
  299. char* get_worst_guess(const vector<char*>& valid_words, bool& result_is_correct_word, const vector<guess>& guesses_so_far, int& rating) {
  300.     if (valid_words.size() == 0) {
  301.         rating = 0;
  302.         return NULL;
  303.     } else if (valid_words.size() == 1) {
  304.         result_is_correct_word = true;
  305.         rating = 0;
  306.         return valid_words[0];
  307.     }
  308.  
  309.     map<string, bool> valid_words_map;
  310.     for (int i = 0; i < valid_words.size(); i++) {
  311.         valid_words_map[valid_words[i]] = true;
  312.     }
  313.  
  314.     char* best_guess = NULL;
  315.     int best_guess_rating = 0;
  316.     bool best_matches_hints = false;
  317.  
  318.     if (should_prefer_dense(valid_words.size())) {
  319.         // Dense case - use direct array indexing
  320.         for (int i = 0; i < guess_words.size(); i++) {
  321.             if (guess_words[i][0] == '\0') {
  322.                 continue; // Word was removed from dictionary during guessing
  323.             }
  324.             memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  325.             bool skip_to_next_word = false;
  326.             for (int j = 0; j < valid_words.size(); j++) {
  327.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  328.                 hint_class_counts[idx]++;
  329.                 // if (hint_class_counts[idx] > best_guess_rating) {
  330.                 //     skip_to_next_word = true;
  331.                 //     break;
  332.                 // }
  333.             }
  334.             if (skip_to_next_word) {
  335.                 continue;
  336.             }
  337.  
  338.             int guess_rating = 0;
  339.             // Rating for this guess is max of all hint class counts
  340.             for (int j = 0; j < num_hint_values; j++) {
  341.                 if (hint_class_counts[j] >= guess_rating) {
  342.                     guess_rating = hint_class_counts[j];
  343.                 }
  344.             }
  345.             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())) {
  346.                 best_guess = guess_words[i];
  347.                 best_guess_rating = guess_rating;
  348.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  349.             } 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())) {
  350.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  351.                 // so far, so that we have a chance to guess the word.
  352.                 best_guess = guess_words[i];
  353.                 best_guess_rating = guess_rating;
  354.                 best_matches_hints = true;
  355.             }
  356.         }
  357.     } else {
  358.         // Sparse case - use map
  359.         map<hint, int> hint_class_counts;
  360.         for (int i = 0; i < guess_words.size(); i++) {
  361.             hint_class_counts.clear();
  362.             bool skip_to_next_word = false;
  363.             for (int j = 0; j < valid_words.size(); j++) {
  364.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  365.                 hint_class_counts[idx]++;
  366.                 // if (hint_class_counts[idx] > best_guess_rating) {
  367.                 //     skip_to_next_word = true;
  368.                 //     break;
  369.                 // }
  370.             }
  371.             if (skip_to_next_word) {
  372.                 continue;
  373.             }
  374.  
  375.             int guess_rating = 0;
  376.             // Rating for this guess is max of all hint class counts
  377.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  378.                 if (iter->second >= guess_rating) {
  379.                     guess_rating = iter->second;
  380.                 }
  381.             }
  382.  
  383.             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())) {
  384.                 best_guess = guess_words[i];
  385.                 best_guess_rating = guess_rating;
  386.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  387.             } 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())) {
  388.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  389.                 // so far, so that we have a chance to guess the word.
  390.                 best_guess = guess_words[i];
  391.                 best_guess_rating = guess_rating;
  392.                 best_matches_hints = true;
  393.             }
  394.         }
  395.     }
  396.  
  397.     result_is_correct_word = false;
  398.     rating = best_guess_rating;
  399.     return best_guess;
  400. }
  401.  
  402. bool can_guess_word_max_depth(const vector<char*>& valid_words, char* word, const vector<guess>& guesses_so_far, int max_depth) {
  403.     if (valid_words.size() <= 1) {
  404.         return true;
  405.     }
  406.     if (max_depth == 0) {
  407.         return false;
  408.     }
  409.  
  410.     for (int i = 0; i < guess_words.size(); i++) {
  411.         if (is_among_guesses(guess_words[i], guesses_so_far)) {
  412.             continue;
  413.         }
  414.         vector<guess> guesses_so_far2 = guesses_so_far;
  415.         guesses_so_far2.push_back(guess(guess_words[i], get_wordle_hints(guess_words[i], word)));
  416.         vector<char*> valid_words2 = get_valid_words(valid_words, guesses_so_far2);
  417.         if (valid_words2.size() == valid_words.size()) {
  418.             continue; // No new information
  419.         }
  420.  
  421.         if (!can_guess_word_max_depth(valid_words2, word, guesses_so_far2, max_depth - 1)) {
  422.             return false;
  423.         }
  424.     }
  425.  
  426.     return true;
  427. }
  428.  
  429. void cache_best_first_guess() {
  430.     if (best_first_guess == NULL) {
  431.         vector<guess> guesses_so_far;
  432.         bool result_is_correct_word;
  433.         int rating;
  434.         cout << "Computing and caching best first guess..." << endl;
  435.         auto start = high_resolution_clock::now();
  436.         best_first_guess = get_best_guess(answer_words, result_is_correct_word, guesses_so_far, rating);
  437.         auto stop = high_resolution_clock::now();
  438.         cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  439.         // best_first_guess = _strdup("TEARS");
  440.     }
  441. }
  442.  
  443. struct pairi {
  444.     int i;
  445.     int j;
  446.  
  447.     bool operator<(const pairi p) const {
  448.         if (i < p.i)
  449.             return true;
  450.         else if (i > p.i)
  451.             return false;
  452.         else
  453.             return j < p.j;
  454.     }
  455. };
  456.  
  457. int thread_num;
  458. int num_threads;
  459.  
  460. void best_first_k() {
  461.     map<hint, int> hint_class_counts;
  462.     int best_worst_count_so_far = 7;
  463.     // int best_worst_count_so_far = 6; // NOLES CRAFT WIMPY
  464.     int best_pair_i = -1, best_pair_j = -1, best_pair_k = -1;
  465.  
  466.     char buf[300];
  467.     sprintf(buf, "results.small.txt", thread_num);
  468.     ofstream of(buf);
  469.     cout << "thread_num: " << thread_num << endl;
  470.     for (int i=thread_num; i < guess_words.size(); i+= num_threads) {
  471.         cout << "i: " << i << " " << guess_words[i] << endl;
  472.         for (int j=i+1; j < guess_words.size(); j++) {
  473.             if ((j % 1000) == 0) {
  474.                 cout << "i: " << i << " " << guess_words[i] << " j: " << j << " " << guess_words[j] << endl;
  475.             }
  476.  
  477.             hint_class_counts.clear();
  478.             for (int m = 0; m < answer_words.size(); m++) {
  479.                 int combined_hint = get_wordle_hints(guess_words[i], answer_words[m]) * num_hint_values +
  480.                     get_wordle_hints(guess_words[j], answer_words[m]);
  481.                 int count = hint_class_counts[combined_hint];
  482.                 hint_class_counts[combined_hint] = count + 1;
  483.             }
  484.  
  485.             int guess_rating = 0;
  486.             vector<guess> guesses_so_far;
  487.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  488.                 if (iter->second >= guess_rating) {
  489.                     guess_rating = iter->second;
  490.                     guesses_so_far.clear();
  491.                     guesses_so_far.push_back(guess(guess_words[i], iter->first / num_hint_values));
  492.                     guesses_so_far.push_back(guess(guess_words[j], iter->first % num_hint_values));
  493.                 }
  494.             }
  495.             vector<char*> valid_words_first_2 = get_valid_words(answer_words, guesses_so_far);
  496.  
  497.             for (int k = j + 1; k < guess_words.size(); k++) {
  498.                 hint_class_counts.clear();
  499.  
  500.                 bool exceeded_count = false;
  501.                 for (int m = 0; m < valid_words_first_2.size(); m++) {
  502.                     int combined_hint = get_wordle_hints(guess_words[i], valid_words_first_2[m]) * num_hint_values * num_hint_values +
  503.                         get_wordle_hints(guess_words[j], valid_words_first_2[m]) * num_hint_values +
  504.                         get_wordle_hints(guess_words[k], valid_words_first_2[m]);
  505.                     int count = hint_class_counts[combined_hint];
  506.                     hint_class_counts[combined_hint] = count + 1;
  507.                     if (count + 1 > best_worst_count_so_far) {
  508.                         exceeded_count = true;
  509.                         break;
  510.                     }
  511.                 }
  512.                 if (exceeded_count) {
  513.                     continue;
  514.                 }
  515.  
  516.                 for (int m = 0; m < answer_words.size(); m++) {
  517.                     int combined_hint = get_wordle_hints(guess_words[i], answer_words[m]) * num_hint_values * num_hint_values +
  518.                         get_wordle_hints(guess_words[j], answer_words[m]) * num_hint_values +
  519.                         get_wordle_hints(guess_words[k], answer_words[m]);
  520.                     int count = hint_class_counts[combined_hint];
  521.                     hint_class_counts[combined_hint] = count + 1;
  522.                     if (count + 1 > best_worst_count_so_far) {
  523.                         break;
  524.                     }
  525.                 }
  526.  
  527.                 int guess_rating = 0;
  528.                 for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  529.                     if (iter->second >= guess_rating) {
  530.                         guess_rating = iter->second;
  531.                     }
  532.                 }
  533.                 if (guess_rating < best_worst_count_so_far) {
  534.                     best_worst_count_so_far = guess_rating;
  535.                     best_pair_i = i;
  536.                     best_pair_j = j;
  537.                     best_pair_k = k;
  538.                     cout << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  539.                     of << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  540.                     of.flush();
  541.                 } else if (guess_rating == best_worst_count_so_far) {
  542.                     cout << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  543.                     of << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  544.                     of.flush();
  545.                 }
  546.             }
  547.         }
  548.     }
  549.  
  550.     cout << guess_words[best_pair_i] << " " << guess_words[best_pair_j] << " " << guess_words[best_pair_k] << " " << best_worst_count_so_far << endl;
  551.     of << guess_words[best_pair_i] << " " << guess_words[best_pair_j] << " " << guess_words[best_pair_k] << " " << best_worst_count_so_far << endl;
  552.     of.flush();
  553. }
  554.  
  555. void find_best_first_guess() {
  556.     cache_best_first_guess();
  557.     cout << "Best first guess: " << best_first_guess << endl;
  558. }
  559.  
  560. void print_words(const vector<char*>& valid_words) {
  561.     cout << "[";
  562.     if (valid_words.size() <= 50) {
  563.         int limit = valid_words.size() <= 50 ? valid_words.size() : 20;
  564.         for (int i = 0; i < limit; i++) {
  565.             cout << valid_words[i] << (i < valid_words.size() - 1 ? ", " : "");
  566.         }
  567.         if (limit < valid_words.size()) {
  568.             cout << "(" << (valid_words.size() - limit) << " other words" << ")";
  569.         }
  570.     } else {
  571.         cout << "(" << valid_words.size() << " words)" << endl;
  572.     }
  573.     cout << "]";
  574. }
  575.  
  576. void guess_unknown_word() {
  577.     vector<guess> guesses_so_far;
  578.     bool result_is_correct_word;
  579.     int rating;
  580.  
  581.     string hint_string;
  582.     char* current_guess;
  583.     do {
  584.         cache_best_first_guess();
  585.         current_guess = best_first_guess;
  586.         cout << "Next guess: " << current_guess << endl;
  587.         cout << "Enter result (k for black, y for yellow, g for green, or ENTER if word is not accepted): ";
  588.         getline(cin, hint_string);
  589.         if (hint_string.length() == 0) {
  590.             strcpy(best_first_guess, ""); // Remove from dictionary
  591.             best_first_guess = NULL;
  592.         }
  593.     } while (hint_string.length() == 0);
  594.     guesses_so_far.push_back(guess(current_guess, string_to_hint(hint_string)));
  595.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  596.     print_words(valid_words);
  597.     cout << endl << endl;
  598.  
  599.     while (true) {
  600.         char* current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  601.         if (current_guess == NULL) {
  602.             cout << "Word is not in dictionary." << endl;
  603.             break;
  604.         } else if (result_is_correct_word) {
  605.             cout << "The word is: " << current_guess << endl;
  606.             break;
  607.         }
  608.         cout << "Next guess: " << current_guess << endl;
  609.         cout << "Enter result (k for black, y for yellow, g for green, or ENTER if word is not accepted): ";
  610.         getline(cin, hint_string);
  611.         if (hint_string.length() == 0) {
  612.             strcpy(current_guess, ""); // Remove from dictionary
  613.             continue;
  614.         }
  615.         guesses_so_far.push_back(guess(current_guess, string_to_hint(hint_string)));
  616.         valid_words = get_valid_words(valid_words, guesses_so_far);
  617.         print_words(valid_words);
  618.         cout << endl << endl;
  619.     }
  620. }
  621.  
  622. void solve_single_word_helper(char* answer) {
  623.     vector<guess> guesses_so_far;
  624.     bool result_is_correct_word;
  625.     int rating;
  626.  
  627.     auto start = high_resolution_clock::now();
  628.     cache_best_first_guess();
  629.     char* current_guess = best_first_guess;
  630.     hint hint = get_wordle_hints(current_guess, answer);
  631.     guesses_so_far.push_back(guess(current_guess, hint));
  632.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  633.     cout << current_guess << " " << hint_to_string(hint) << " ";
  634.     print_words(valid_words);
  635.     cout << endl << endl;
  636.  
  637.     while (strcmp(current_guess, answer) != 0) {
  638.         current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  639.         if (current_guess == NULL) {
  640.             cout << "Word is not in dictionary. You may have typed it incorrectly." << endl;
  641.             break;
  642.         }
  643.         hint = get_wordle_hints(current_guess, answer);
  644.         guesses_so_far.push_back(guess(current_guess, hint));
  645.         valid_words = get_valid_words(valid_words, guesses_so_far);
  646.         cout << current_guess << " " << hint_to_string(hint) << " ";
  647.         print_words(valid_words);
  648.         cout << endl << endl;
  649.     }
  650.     auto stop = high_resolution_clock::now();
  651.     cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  652.     cout << "Number of guesses: " << guesses_so_far.size() << endl;
  653. }
  654.  
  655. void solve_single_word() {
  656.     char* answer = NULL;
  657.     do {
  658.         cout << "Enter solution word: ";
  659.         string answer_str;
  660.         getline(cin, answer_str);
  661.         answer = _strdup(answer_str.c_str());
  662.         toupper_cstr(answer);
  663.         if (strlen(answer) != word_length) {
  664.             cout << "Word is wrong length, must be " << word_length << " letters." << endl;
  665.             continue;
  666.         }
  667.         break;
  668.     } while (true);
  669.  
  670.     solve_single_word_helper(answer);
  671. }
  672.  
  673. void solve_all_words_in_dictionary() {
  674.     vector<guess> guesses_so_far;
  675.     bool result_is_correct_word;
  676.     int rating;
  677.  
  678.     auto overall_start = high_resolution_clock::now();
  679.  
  680.     double best_average = 100000.0f;
  681.     char* best_first_word_solve_all = NULL;
  682.     char* best_second_word_solve_all = NULL;
  683.     char* best_third_word_solve_all = NULL;
  684.  
  685.     ifstream in("solutions6.txt");
  686.  
  687.     while (true) {
  688.         cout << "Precomputing best first guess..." << endl;
  689.         cache_best_first_guess();
  690.         // char* first_guess = best_first_guess;
  691.         string line;
  692.         getline(in, line);
  693.         if (in.eof()) break;
  694.         if (line.length() < word_length * 3 + 2) {
  695.             continue;
  696.         }
  697.         char* first_guess = _strdup(line.c_str());
  698.         first_guess[word_length] = '\0';
  699.         char* second_guess = _strdup(line.c_str() + word_length + 1);
  700.         second_guess[word_length] = '\0';
  701.         char* third_guess = _strdup(line.c_str() + word_length + 1 + word_length + 1);
  702.         third_guess[word_length] = '\0';
  703.         cout << "First guess: " << first_guess << endl;
  704.         // first_guess = _strdup("TAILS");
  705.  
  706. #if 0
  707.         {
  708.             vector<guess> guesses_so_far_first;
  709.             char* foo;
  710.             foo = _strdup("-----");
  711.             for (int i = 0; i < word_length; i++) {
  712.                 for (int j = i + 1; j < word_length; j++) {
  713.                     foo[0] = first_guess[i];
  714.                     foo[1] = first_guess[j];
  715.                     guesses_so_far_first.clear();
  716.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("yykkk")));
  717.                     cout << " " << first_guess[i] << " " << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  718.                     guesses_so_far_first.clear();
  719.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("ykkkk")));
  720.                     cout << " " << first_guess[i] << "!" << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  721.                     guesses_so_far_first.clear();
  722.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("kykkk")));
  723.                     cout << "!" << first_guess[i] << " " << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  724.                     guesses_so_far_first.clear();
  725.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("kkkkk")));
  726.                     cout << "!" << first_guess[i] << "!" << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  727.                     cout << endl;
  728.                 }
  729.             }
  730.         }
  731.  
  732.         {
  733.             vector<guess> guesses_so_far_first;
  734.             char* foo;
  735.             foo = _strdup("-----");
  736.             char* bar;
  737.             bar = _strdup("kkkkk");
  738.             for (int i = 0; i < word_length; i++) {
  739.                 for (int j = 0; j < word_length; j++) {
  740.                     guesses_so_far_first.clear();
  741.                     foo[j] = first_guess[i];
  742.                     bar[j] = 'g';
  743.                     guesses_so_far_first.push_back(guess(foo, string_to_hint(bar)));
  744.                     cout << first_guess[i] << " " << j << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  745.                     bar[j] = 'k';
  746.                     foo[j] = '-';
  747.                 }
  748.             }
  749.         }
  750. #endif
  751.  
  752.         char** second_guesses = NULL;
  753.         int empty_classes = 0;
  754. #if 0
  755.         if (answer_words.size() * 1000 > num_hint_values) {
  756.             cout << "Precomputing best second guesses..." << endl;
  757.             auto start = high_resolution_clock::now();
  758.             second_guesses = new char* [num_hint_values];
  759.             for (int i = 0; i < num_hint_values; i++) {
  760.                 vector<guess> guesses_so_far;
  761.                 guesses_so_far.push_back(guess(first_guess, i));
  762.                 vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  763.                 // second_guesses[i] = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  764.                 second_guesses[i] = _strdup("DECOR");
  765. #if 0
  766.                 cout << hint_to_string(i) << " " << valid_words.size() << " " << (second_guesses[i] == NULL ? "" : second_guesses[i]) << endl;
  767. #endif
  768.                 if (valid_words.size() == 0) {
  769.                     empty_classes++;
  770.                 }
  771.                 // print_words(valid_words);
  772.                 // cout << endl;
  773.  
  774.                 // if ((i % 1000) == 0) {
  775.                 //     auto stop = high_resolution_clock::now();
  776.                 //     cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * num_hint_values << " s" << endl;
  777.                 // }
  778.             }
  779.             auto stop = high_resolution_clock::now();
  780.             cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  781.             cout << "Empty hint classes: " << empty_classes << endl;
  782.         }
  783. #endif
  784.  
  785.         char*** third_guesses = NULL;
  786. #if 0
  787.         if (sqrt(answer_words.size() * 1000) > num_hint_values) {
  788.             cout << "Precomputing third guesses..." << endl;
  789.             auto start = high_resolution_clock::now();
  790.             third_guesses = new char** [num_hint_values * num_hint_values];
  791.             for (int i = 0; i < num_hint_values; i++) {
  792.                 vector<guess> guesses_so_far_first;
  793.                 guesses_so_far_first.push_back(guess(first_guess, i));
  794.                 vector<char*> valid_words_first = get_valid_words(answer_words, guesses_so_far_first);
  795.  
  796.                 third_guesses[i] = new char* [num_hint_values];
  797.                 for (int j = 0; j < num_hint_values; j++) {
  798.                     vector<guess> guesses_so_far;
  799.                     guesses_so_far.push_back(guess(first_guess, i));
  800.                     // guesses_so_far.push_back(guess(second_guesses[i], j));
  801.                     guesses_so_far.push_back(guess(second_guess, i));
  802.                     vector<char*> valid_words = get_valid_words(valid_words_first, guesses_so_far);
  803.                     third_guesses[i][j] = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  804.  
  805. #if 0
  806.                     if (valid_words.size() > 10) {
  807.                         cout << hint_to_string(i) << " " << (second_guesses[i] == NULL ? "null" : second_guesses[i]) << " " << hint_to_string(j) << " " << valid_words.size() << endl;
  808.                     }
  809. #endif
  810.                 }
  811.                 // if ((i % 1000) == 0) {
  812.                 //     auto stop = high_resolution_clock::now();
  813.                 //     cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * num_hint_values << " s" << endl;
  814.                 // }
  815.             }
  816.             auto stop = high_resolution_clock::now();
  817.             cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  818.         }
  819. #endif
  820.  
  821.         int max_guesses = 0;
  822.         char* worst_word = NULL;
  823.  
  824.         cout << "Solving all words in dictionary..." << endl;
  825.         auto start = high_resolution_clock::now();
  826.         int histogram[50] = { 0 };
  827.         for (int i = 0; i < answer_words.size(); i++) {
  828.             if ((i % 1000) == 0) {
  829.                 cout << i << endl;
  830.                 // auto stop = high_resolution_clock::now();
  831.                 // cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * answer_words.size() << " s" << endl;
  832.             }
  833.             vector<guess> guesses_so_far;
  834.             char* current_guess = first_guess;
  835.  
  836.             guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  837.  
  838.             int number_of_guesses = 1;
  839.             hint first_hint;
  840.             vector<char*> valid_words;
  841.  
  842.             if (strcmp(current_guess, answer_words[i]) != 0) {
  843.                 first_hint = get_wordle_hints(current_guess, answer_words[i]);
  844.                 if (second_guesses != NULL) {
  845.                     // current_guess = second_guesses[first_hint];
  846.                 } else {
  847.                     // current_guess = get_best_guess(answer_words, result_is_correct_word, guesses_so_far, rating);
  848.                 }
  849.                 current_guess = second_guess;
  850.                 number_of_guesses++;
  851.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  852.                 valid_words = get_valid_words(answer_words, guesses_so_far);
  853.             }
  854.  
  855.             if (/*third_guesses != NULL &&*/ strcmp(current_guess, answer_words[i]) != 0) {
  856.                 // current_guess = third_guesses[first_hint][get_wordle_hints(current_guess, answer_words[i])];
  857.                 current_guess = third_guess;
  858.                 number_of_guesses++;
  859.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  860.                 valid_words = get_valid_words(valid_words, guesses_so_far);
  861.             }
  862.  
  863.             while (strcmp(current_guess, answer_words[i]) != 0) {
  864.                 current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  865.                 number_of_guesses++;
  866.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  867.                 valid_words = get_valid_words(valid_words, guesses_so_far);
  868.             }
  869.  
  870.             histogram[number_of_guesses]++;
  871.             if (number_of_guesses > max_guesses) {
  872.                 worst_word = answer_words[i];
  873.                 max_guesses = number_of_guesses;
  874.             }
  875.  
  876. #if 0
  877.             if (number_of_guesses == 5) {
  878.                 cout << "    " << answer_words[i] << ": ";
  879.                 for (int i = 0; i < guesses_so_far.size(); i++) {
  880.                     cout << guesses_so_far[i].word << " " << hint_to_string(guesses_so_far[i].hint) << "  ";
  881.                 }
  882.                 cout << endl;
  883.             }
  884. #endif
  885.         }
  886.         auto stop = high_resolution_clock::now();
  887.         cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  888.  
  889.         auto overall_stop = high_resolution_clock::now();
  890.         cout << "Total time: " << duration_cast<microseconds>(overall_stop - overall_start).count() / 1000000.0 << " s" << endl;
  891.         cout << "Time per word: " << duration_cast<microseconds>(overall_stop - overall_start).count() / 1000.0 / answer_words.size() << " ms" << endl;
  892.  
  893.         int total_count = 0, total_guesses = 0;
  894.         for (int i = 0; i < sizeof(histogram) / sizeof(histogram[0]); i++) {
  895.             if (histogram[i] != 0) {
  896.                 cout << i << ": " << histogram[i] << endl;
  897.                 total_count += histogram[i];
  898.                 total_guesses += i * histogram[i];
  899.             }
  900.         }
  901.         double average = double(total_guesses) / total_count;
  902.         cout << "Average: " << average << " guesses" << endl;
  903.         cout << "Worst word was " << worst_word << ":" << endl;
  904.         // solve_single_word_helper(worst_word);
  905.  
  906.         if (average < best_average) {
  907.             best_first_word_solve_all = first_guess;
  908.             best_second_word_solve_all = second_guess;
  909.             best_third_word_solve_all = third_guess;
  910.             best_average = average;
  911.         }
  912.         cout << "Best triple so far: " << best_first_word_solve_all << " " << best_second_word_solve_all << " " << best_third_word_solve_all << " with average of " << best_average << endl;
  913.  
  914.         forbid_first_guess(first_guess);
  915.         // break;
  916.     }
  917. }
  918.  
  919. void show_largest_hint_class() {
  920.     cout << "Enter three words:" << endl;
  921.     vector<char*> words;
  922.     for (int i = 0; i < 3; i++) {
  923.         string word;
  924.         cout << "> ";
  925.         getline(cin, word);
  926.         for (int i = 0; i < guess_words.size(); i++) {
  927.             if (strcmp(guess_words[i], word.c_str()) == 0) {
  928.                 words.push_back(guess_words[i]);
  929.             }
  930.         }
  931.     }
  932.  
  933.     map<hint, int> hint_class_counts;
  934.     // int best_worst_count_so_far = 8;
  935.     int best_worst_count_so_far = 6; // NOLES CRAFT WIMPY
  936.     int best_pair_i = -1, best_pair_j = -1, best_pair_k = -1;
  937.  
  938.     for (int m = 0; m < answer_words.size(); m++) {
  939.         int combined_hint = 0;
  940.         for (int i = words.size() - 1; i >= 0; i--) {
  941.             combined_hint *= num_hint_values;
  942.             combined_hint += get_wordle_hints(words[i], answer_words[m]);
  943.         }
  944.         hint_class_counts[combined_hint]++;
  945.     }
  946.  
  947.     int guess_rating = 0;
  948.     vector<guess> guesses_so_far;
  949.     for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  950.         if (iter->second >= guess_rating) {
  951.             guess_rating = iter->second;
  952.             guesses_so_far.clear();
  953.            
  954.             hint combined_hint = iter->first;
  955.             for (int i = 0; i < words.size(); i++) {
  956.                 guesses_so_far.push_back(guess(words[i], combined_hint % num_hint_values));
  957.                 combined_hint /= num_hint_values;
  958.             }
  959.         }
  960.     }
  961.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  962.  
  963.     for (int i = 0; i < guesses_so_far.size(); i++) {
  964.         cout << guesses_so_far[i].word << " " << hint_to_string(guesses_so_far[i].hint) << endl;
  965.     }
  966.     cout << endl;
  967.     print_words(valid_words);
  968.     cout << endl;
  969. }
  970.  
  971. int main(int argc, char* argv[]) {
  972. #if 0
  973.     for (word_length = 1; word_length <= 500; word_length++) {
  974.         num_hint_values = 1;
  975.         for (int i = 0; i < word_length; i++) {
  976.             num_hint_values *= 3;
  977.         }
  978.  
  979.         guess_words = read_word_file(GUESS_WORD_FILE);
  980.         if (guess_words.size() > 0) {
  981.             cout << "Valid guesses : " << guess_words.size() << " words of length " << word_length << " in " << GUESS_WORD_FILE << endl;
  982.         }
  983.         if (strcmp(GUESS_WORD_FILE, ANSWER_WORD_FILE) == 0) {
  984.             answer_words = guess_words;
  985.         } else {
  986.             answer_words = read_word_file(ANSWER_WORD_FILE);
  987.         }
  988.         if (answer_words.size() > 0) {
  989.             cout << "Valid answers : " << answer_words.size() << " words of length " << word_length << " in " << ANSWER_WORD_FILE << endl;
  990.         }
  991.         if (answer_words.size() == 0 || guess_words.size() == 0) {
  992.             continue;
  993.         }
  994.  
  995.         if (should_prefer_dense(answer_words.size())) {
  996.             hint_class_counts = new int[num_hint_values];
  997.         }
  998.  
  999.         best_first_guess = NULL;
  1000.         find_best_first_guess();
  1001.  
  1002.         delete[] hint_class_counts;
  1003.         hint_class_counts = NULL;
  1004.     }
  1005.     return 0;
  1006. #endif
  1007.  
  1008.     string newline;
  1009.     // cout << "Enter word length: ";
  1010.     // cin >> word_length;
  1011.     // getline(cin, newline);
  1012.     word_length = 5;
  1013.     num_hint_values = 1;
  1014.     for (int i = 0; i < word_length; i++) {
  1015.         num_hint_values *= 3;
  1016.     }
  1017.  
  1018.     guess_words = read_word_file(GUESS_WORD_FILE);
  1019.     cout << "Valid guesses : " << guess_words.size() << " words of length " << word_length << " in " << GUESS_WORD_FILE << endl;
  1020.     if (strcmp(GUESS_WORD_FILE, ANSWER_WORD_FILE) == 0) {
  1021.         answer_words = guess_words;
  1022.     } else {
  1023.         answer_words = read_word_file(ANSWER_WORD_FILE);
  1024.     }
  1025.     cout << "Valid answers : " << answer_words.size() << " words of length " << word_length << " in " << ANSWER_WORD_FILE << endl;
  1026.  
  1027.     if (answer_words.size() == 0 || guess_words.size() == 0) {
  1028.         cout << "No words of length " << word_length << " in wordlists, exiting." << endl;
  1029.         return 1;
  1030.     }
  1031.  
  1032.     if (should_prefer_dense(answer_words.size())) {
  1033.         hint_class_counts = new int[num_hint_values];
  1034.     }
  1035.  
  1036.     // thread_num = atoi(argv[1]);
  1037.     // num_threads = atoi(argv[2]);
  1038.     // thread_num = 0;
  1039.     // num_threads = 1;
  1040.     // best_first_k();
  1041.     // return 0;
  1042.  
  1043.     while (true) {
  1044.         cout << "Select one:" << endl;
  1045.         cout << " 1. Find best first guess" << endl;
  1046.         cout << " 2. Guess unknown word (solve puzzle)" << endl;
  1047.         cout << " 3. Show solution for a single given word" << endl;
  1048.         cout << " 4. Solve all words in dictionary and show statistics" << endl;
  1049.         cout << " 5. Find best set of 2 words to start out with" << endl;
  1050.         cout << " 6. Show largest hint class of a set of words" << endl;
  1051.         cout << " 7. Quit" << endl;
  1052.         cout << "Enter your choice: ";
  1053.         int choice;
  1054.         cin >> choice;
  1055.         getline(cin, newline);
  1056.  
  1057.         switch (choice) {
  1058.         case 1:
  1059.             find_best_first_guess();
  1060.             break;
  1061.         case 2:
  1062.             guess_unknown_word();
  1063.             break;
  1064.         case 3:
  1065.             solve_single_word();
  1066.             break;
  1067.         case 4:
  1068.             solve_all_words_in_dictionary();
  1069.             break;
  1070.         case 5:
  1071.             best_first_k();
  1072.         case 6:
  1073.             show_largest_hint_class();
  1074.             break;
  1075.         case 7:
  1076.             return 0;
  1077.         default:
  1078.             cout << "Invalid option selected." << endl;
  1079.             break;
  1080.         }
  1081.     }
  1082. }
  1083.  
Advertisement
Add Comment
Please, Sign In to add comment