ChiaraStellata

Wordle Optimal Solver (C++)

Jan 30th, 2022
391
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 103.61 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. #include <algorithm>
  9. #include <cassert>
  10. #include <numeric>
  11.  
  12. using namespace std;
  13. using namespace std::chrono;
  14.  
  15. #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
  16. #define release_assert(x)  if (!(x)) { __debugbreak(); exit(1); }
  17. #else
  18. #define release_assert(x)  if (!(x)) { throw new exception(); }
  19. #endif
  20.  
  21. // From https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt
  22. // const char* ANSWER_WORD_FILE = "sgb-words.txt";
  23. // const char* GUESS_WORD_FILE = "sgb-words.txt";
  24. // From https://boardgames.stackexchange.com/a/38386/3054
  25. // const char* ANSWER_WORD_FILE = "Collins Scrabble Words (2019).txt";
  26. // const char* GUESS_WORD_FILE = "Collins Scrabble Words (2019).txt";
  27. // From http://www.mieliestronk.com/corncob_caps.txt
  28. // const char* ANSWER_WORD_FILE = "corncob_caps.txt";
  29. // const char* GUESS_WORD_FILE = "corncob_caps.txt";
  30. // From https://github.com/first20hours/google-10000-english/blob/master/google-10000-english.txt
  31. // const char* ANSWER_WORD_FILE = "google-10000-english.txt";
  32. // const char* GUESS_WORD_FILE = "google-10000-english.txt";
  33. // From https://github.com/dwyl/english-words
  34. // const char* ANSWER_WORD_FILE = "words.txt";
  35. // const char* GUESS_WORD_FILE = "words.txt";
  36. // From https://github.com/Kinkelin/WordleCompetition/blob/main/data/official/combined_wordlist.txt
  37. // const char* ANSWER_WORD_FILE = "combined_wordlist.txt";
  38. // const char* GUESS_WORD_FILE = "combined_wordlist.txt";
  39. // const char* GUESS_WORD_FILE = "combined_wordlist_rearranged.txt";
  40. const char* GUESS_WORD_FILE = "combined_wordlist_real_wordles_first.txt";
  41. // From https://github.com/Kinkelin/WordleCompetition/blob/main/data/official/shuffled_real_wordles.txt
  42. // const char* ANSWER_WORD_FILE = "shuffled_real_wordles.txt";
  43. // const char* GUESS_WORD_FILE = "shuffled_real_wordles.txt";
  44. const char* ANSWER_WORD_FILE = "shuffled_real_wordles_best_first.txt";
  45. // const char* GUESS_WORD_FILE = "shuffled_real_wordles_best_first.txt";
  46.  
  47. int word_length;
  48. vector<char*> guess_words;
  49. vector<int> guess_words_letter_masks;
  50. vector<char*> answer_words;
  51. char* best_first_guess = NULL;
  52.  
  53. bool all_alpha(string s) {
  54.     for (unsigned int i = 0; i < s.length(); i++) {
  55.         if (!isalpha(s[i])) {
  56.             return false;
  57.         }
  58.     }
  59.     return true;
  60. }
  61.  
  62. void toupper_cstr(char* s) {
  63.     while (*s != '\0') {
  64.         *s = toupper(*s);
  65.         s++;
  66.     }
  67. }
  68.  
  69. const int MEMORY_POOL_WORDS = 200000; // Enough even for largest dictionary
  70. char* memory_pool = NULL;
  71. char* memory_pool_next;
  72. map<string, char*> tokenized_words;
  73.  
  74. vector<char*> read_word_file(const char* filename) {
  75.     if (memory_pool == NULL) {
  76.         memory_pool = new char[(word_length + 1) * MEMORY_POOL_WORDS];
  77.         memory_pool_next = memory_pool;
  78.     }
  79.     vector<char*> result;
  80.     string line;
  81.     ifstream infile(filename);
  82.     int max_length = 0;
  83.     while (getline(infile, line)) {
  84.         if (all_alpha(line)) {
  85.             if (line.length() == word_length) {
  86.                 char* word = memory_pool_next;
  87.                 strcpy(word, line.c_str());
  88.                 memory_pool_next += word_length + 1;
  89.                 if (memory_pool_next + word_length + 1 >= memory_pool + (word_length + 1) * MEMORY_POOL_WORDS) {
  90.                     cout << "Memory pool exceeded, increase MEMORY_POOL_WORDS" << endl;
  91.                     exit(1);
  92.                 }
  93.                 toupper_cstr(word);
  94.  
  95.                 if (tokenized_words.find(word) != tokenized_words.end()) {
  96.                     result.push_back(tokenized_words[word]);
  97.                 } else {
  98.                     tokenized_words[word] = word;
  99.                     result.push_back(word);
  100.                 }
  101.             }
  102.         }
  103.     }
  104.     return result;
  105. }
  106.  
  107. // Hints are packed into unsigned integers, one ternary digit per bit, least-significant digit is last character
  108. // This is more space-efficient than using two bits per hint, which is important for cache efficiency.
  109. typedef uint64_t hint; // Use for number of words >= 21
  110. // typedef uint32_t hint; // Use for number of words <= 20
  111. hint num_hint_values; // max hint value plus one
  112.  
  113. hint get_wordle_hints(const char* guess, const char* answer) {
  114.     hint result = 0;
  115.     char buf[5];
  116.     int buf_size = 0;
  117.  
  118.     for (int i = 0; i < 5; i++) {
  119.         if (guess[i] != answer[i]) {
  120.             buf[buf_size] = answer[i];
  121.             buf_size++;
  122.         }
  123.     }
  124.     for (int i = 0; i < 5; i++) {
  125.         result *= 3;
  126.         if (guess[i] == answer[i]) {
  127.             result += 2; // green
  128.         } else {
  129.             for (int j = 0; j < buf_size; j++) {
  130.                 if (guess[i] == buf[j]) {
  131.                     result += 1;
  132.                     buf[j] = ' ';
  133.                     break;
  134.                 }
  135.             }
  136.  
  137.             // Otherwise black/grey which is zero, do nothing
  138.         }
  139.     }
  140.     return result;
  141. }
  142.  
  143. hint string_to_hint(string hint_str) {
  144.     hint result = 0;
  145.     for (int i = 0; i < hint_str.length(); i++) {
  146.         result *= 3;
  147.         if (hint_str[i] == 'g') {
  148.             result += 2;
  149.         } else if (hint_str[i] == 'y') {
  150.             result += 1;
  151.         }
  152.     }
  153.     return result;
  154. }
  155.  
  156. string hint_to_string(hint hint) {
  157.     string result;
  158.     for (int i = 0; i < word_length; i++) {
  159.         char c;
  160.         if ((hint % 3) == 2) {
  161.             c = 'g';
  162.         } else if ((hint % 3) == 1) {
  163.             c = 'y';
  164.         } else {
  165.             c = '-';
  166.         }
  167.         result = c + result;
  168.         hint /= 3;
  169.     }
  170.     return result;
  171. }
  172.  
  173. struct guess {
  174.     guess(char* word, hint hint) {
  175.         this->word = word;
  176.         this->hint = hint;
  177.     }
  178.  
  179.     guess(char* guess_word, char* answer_word) {
  180.         this->word = guess_word;
  181.         this->hint = get_wordle_hints(guess_word, answer_word);
  182.     }
  183.  
  184.     char* word;
  185.     hint hint;
  186. };
  187.  
  188. bool matches_hints(const char* answer, const vector<guess>& guesses_so_far) {
  189.     for (int i = 0; i < guesses_so_far.size(); i++) {
  190.         if (get_wordle_hints(guesses_so_far[i].word, answer) != guesses_so_far[i].hint) {
  191.             return false;
  192.         }
  193.     }
  194.     return true;
  195. }
  196.  
  197. // In Wordle hard mode, black/grey hints do *not* have to be used, only yellow and green
  198. bool matches_hints_guess(const char* guess_word, const vector<guess>& guesses_so_far) {
  199.     for (int i = 0; i < guesses_so_far.size(); i++) {
  200.         hint h1 = guesses_so_far[i].hint;
  201.         hint h2 = get_wordle_hints(guesses_so_far[i].word, guess_word);
  202.  
  203.         for (int i = 0; i < word_length; i++) {
  204.             int color1 = h1 % 3;
  205.             int color2 = h2 % 3;
  206.             if (color1 != 0 && color1 != color2) {
  207.                 return false;
  208.             }
  209.             h1 /= 3;
  210.             h2 /= 3;
  211.         }
  212.     }
  213.     return true;
  214. }
  215.  
  216. int count_valid_words(const vector<char*>& answer_words, const vector<guess>& guesses_so_far) {
  217.     int count = 0;
  218.     for (int i = 0; i < answer_words.size(); i++) {
  219.         if (matches_hints(answer_words[i], guesses_so_far)) {
  220.             count++;
  221.         }
  222.     }
  223.     return count;
  224. }
  225.  
  226. vector<char*> get_valid_words(const vector<char*>& answer_words, const vector<guess>& guesses_so_far) {
  227.     vector<char*> result;
  228.     for (int i = 0; i < answer_words.size(); i++) {
  229.         if (matches_hints(answer_words[i], guesses_so_far)) {
  230.             result.push_back(answer_words[i]);
  231.         }
  232.     }
  233.     return result;
  234. }
  235.  
  236. vector<char*> get_valid_words(const vector<char*>& answer_words, char* guess_word, hint h) {
  237.     vector<char*> result;
  238.     for (int i = 0; i < answer_words.size(); i++) {
  239.         if (get_wordle_hints(guess_word, answer_words[i]) == h) {
  240.             result.push_back(answer_words[i]);
  241.         }
  242.     }
  243.     return result;
  244. }
  245.  
  246.  
  247. vector<char*> get_valid_guesses(const vector<char*>& guess_words, const vector<guess>& guesses_so_far) {
  248.     vector<char*> result;
  249.     for (int i = 0; i < guess_words.size(); i++) {
  250.         if (matches_hints_guess(guess_words[i], guesses_so_far)) {
  251.             result.push_back(guess_words[i]);
  252.         }
  253.     }
  254.     return result;
  255. }
  256.  
  257. inline bool should_prefer_dense(int num_words) {
  258.      return (num_hint_values / num_words) < 250;  // Experimentally determined constant
  259. }
  260.  
  261. bool is_among_guesses(char* word, const vector<guess>& guesses_so_far) {
  262.     for (int i = 0; i < guesses_so_far.size(); i++) {
  263.         if (strcmp(word, guesses_so_far[i].word) == 0) {
  264.             return true;
  265.         }
  266.     }
  267.     return false;
  268. }
  269.  
  270. map<char*, bool> forbidden_first_guesses;
  271.  
  272. void forbid_first_guess(char* word) {
  273.     forbidden_first_guesses[word] = true;
  274.     best_first_guess = NULL;
  275. }
  276.  
  277. int* hint_class_counts = NULL;
  278. char* get_best_guess(const vector<char*>& valid_words, bool& result_is_correct_word, const vector<guess>& guesses_so_far, int& rating) {
  279.     if (valid_words.size() == 0) {
  280.         rating = 0;
  281.         return NULL;
  282.     } else if (valid_words.size() == 1) {
  283.         result_is_correct_word = true;
  284.         rating = 0;
  285.         return valid_words[0];
  286.     }
  287.  
  288.     map<string, bool> valid_words_map;
  289.     for (int i = 0; i < valid_words.size(); i++) {
  290.         valid_words_map[valid_words[i]] = true;
  291.     }
  292.  
  293.     char* best_guess = NULL;
  294.     int best_guess_rating = INT_MAX;
  295.     bool best_matches_hints = false;
  296.  
  297.     if (should_prefer_dense(valid_words.size())) {
  298.         // Dense case - use direct array indexing
  299.         for (int i = 0; i < guess_words.size(); i++) {
  300.             if (guess_words[i][0] == '\0') {
  301.                 continue; // Word was removed from dictionary during guessing
  302.             }
  303.             memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  304.             bool skip_to_next_word = false;
  305.             for (int j = 0; j < valid_words.size(); j++) {
  306.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  307.                 hint_class_counts[idx]++;
  308.                 if (hint_class_counts[idx] > best_guess_rating) {
  309.                     skip_to_next_word = true;
  310.                     break;
  311.                 }
  312.             }
  313.             if (skip_to_next_word) {
  314.                 continue;
  315.             }
  316.  
  317.             int guess_rating = 0;
  318.             // Rating for this guess is max of all hint class counts
  319.             for (int j = 0; j < num_hint_values; j++) {
  320.                 if (hint_class_counts[j] >= guess_rating) {
  321.                     guess_rating = hint_class_counts[j];
  322.                 }
  323.             }
  324.             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())) {
  325.                 best_guess = guess_words[i];
  326.                 best_guess_rating = guess_rating;
  327.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  328.             } 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())) {
  329.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  330.                 // so far, so that we have a chance to guess the word.
  331.                 best_guess = guess_words[i];
  332.                 best_guess_rating = guess_rating;
  333.                 best_matches_hints = true;
  334.             }
  335.         }
  336.     } else {
  337.         // Sparse case - use map
  338.         map<hint, int> hint_class_counts;
  339.         for (int i = 0; i < guess_words.size(); i++) {
  340.             hint_class_counts.clear();
  341.             bool skip_to_next_word = false;
  342.             for (int j = 0; j < valid_words.size(); j++) {
  343.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  344.                 hint_class_counts[idx]++;
  345.                 if (hint_class_counts[idx] > best_guess_rating) {
  346.                     skip_to_next_word = true;
  347.                     break;
  348.                 }
  349.             }
  350.             if (skip_to_next_word) {
  351.                 continue;
  352.             }
  353.  
  354.             int guess_rating = 0;
  355.             // Rating for this guess is max of all hint class counts
  356.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  357.                 if (iter->second >= guess_rating) {
  358.                     guess_rating = iter->second;
  359.                 }
  360.             }
  361.  
  362.             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())) {
  363.                 best_guess = guess_words[i];
  364.                 best_guess_rating = guess_rating;
  365.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  366.             } 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())) {
  367.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  368.                 // so far, so that we have a chance to guess the word.
  369.                 best_guess = guess_words[i];
  370.                 best_guess_rating = guess_rating;
  371.                 best_matches_hints = true;
  372.             }
  373.         }
  374.     }
  375.  
  376.     result_is_correct_word = false;
  377.     rating = best_guess_rating;
  378.     return best_guess;
  379. }
  380.  
  381. char* get_perfect_split(const vector<char*>& valid_words, bool& is_valid_word) {
  382.     assert(valid_words.size() > 1);
  383.  
  384.     if (valid_words.size() > num_hint_values) {
  385.         return NULL;
  386.     }
  387.  
  388.     vector<int> valid_words_letter_masks;
  389.     valid_words_letter_masks.resize(valid_words.size());
  390.     for (int i = 0; i < valid_words.size(); i++) {
  391.         int letter_mask = 0;
  392.         for (int cidx = 0; cidx < word_length; cidx++) {
  393.             letter_mask |= 1 << (valid_words[i][cidx] - 'A');
  394.         }
  395.         valid_words_letter_masks[i] = letter_mask;
  396.     }
  397.  
  398.     for (int i = 0; i < valid_words.size(); i++) {
  399.         // Check first for two kkkkk quickly using bitmaps
  400.         int count = 0;
  401.         for (int j = 0; j < valid_words.size(); j++) {
  402.             if ((valid_words_letter_masks[j] & valid_words_letter_masks[i]) == 0) {
  403.                 count++;
  404.                 if (count >= 2) break;
  405.             }
  406.         }
  407.         if (count >= 2) {
  408.             continue;
  409.         }
  410.  
  411.         memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  412.         bool found_perfect_split = true;
  413.         for (int j = 0; j < valid_words.size(); j++) {
  414.             int idx = get_wordle_hints(valid_words[i], valid_words[j]);
  415.             hint_class_counts[idx]++;
  416.             if (hint_class_counts[idx] > 1) {
  417.                 found_perfect_split = false;
  418.                 break;
  419.             }
  420.         }
  421.         if (found_perfect_split) {
  422.             is_valid_word = true;
  423.             return valid_words[i];
  424.         }
  425.     }
  426.  
  427.     for (int i = 0; i < guess_words.size(); i++) {
  428.         // Check first for two kkkkk quickly using bitmaps
  429.         int count = 0;
  430.         for (int j = 0; j < valid_words.size(); j++) {
  431.             if ((valid_words_letter_masks[j] & guess_words_letter_masks[i]) == 0) {
  432.                 count++;
  433.                 if (count >= 2) break;
  434.             }
  435.         }
  436.         if (count >= 2) {
  437.             continue;
  438.         }
  439.  
  440.         memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  441.         bool found_perfect_split = true;
  442.         for (int j = 0; j < valid_words.size(); j++) {
  443.             int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  444.             hint_class_counts[idx]++;
  445.             if (hint_class_counts[idx] > 1) {
  446.                 found_perfect_split = false;
  447.                 break;
  448.             }
  449.         }
  450.         if (found_perfect_split) {
  451.             is_valid_word = false;
  452.             return guess_words[i];
  453.         }
  454.     }
  455.  
  456.     is_valid_word = false;
  457.     return NULL;
  458. }
  459.  
  460. char* get_worst_guess(const vector<char*>& valid_words, bool& result_is_correct_word, const vector<guess>& guesses_so_far, int& rating) {
  461.     if (valid_words.size() == 0) {
  462.         rating = 0;
  463.         return NULL;
  464.     } else if (valid_words.size() == 1) {
  465.         result_is_correct_word = true;
  466.         rating = 0;
  467.         return valid_words[0];
  468.     }
  469.  
  470.     map<string, bool> valid_words_map;
  471.     for (int i = 0; i < valid_words.size(); i++) {
  472.         valid_words_map[valid_words[i]] = true;
  473.     }
  474.  
  475.     char* best_guess = NULL;
  476.     int best_guess_rating = 0;
  477.     bool best_matches_hints = false;
  478.  
  479.     if (should_prefer_dense(valid_words.size())) {
  480.         // Dense case - use direct array indexing
  481.         for (int i = 0; i < guess_words.size(); i++) {
  482.             if (guess_words[i][0] == '\0') {
  483.                 continue; // Word was removed from dictionary during guessing
  484.             }
  485.             memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  486.             bool skip_to_next_word = false;
  487.             for (int j = 0; j < valid_words.size(); j++) {
  488.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  489.                 hint_class_counts[idx]++;
  490.                 // if (hint_class_counts[idx] > best_guess_rating) {
  491.                 //     skip_to_next_word = true;
  492.                 //     break;
  493.                 // }
  494.             }
  495.             if (skip_to_next_word) {
  496.                 continue;
  497.             }
  498.  
  499.             int guess_rating = 0;
  500.             // Rating for this guess is max of all hint class counts
  501.             for (int j = 0; j < num_hint_values; j++) {
  502.                 if (hint_class_counts[j] >= guess_rating) {
  503.                     guess_rating = hint_class_counts[j];
  504.                 }
  505.             }
  506.             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())) {
  507.                 best_guess = guess_words[i];
  508.                 best_guess_rating = guess_rating;
  509.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  510.             } 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())) {
  511.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  512.                 // so far, so that we have a chance to guess the word.
  513.                 best_guess = guess_words[i];
  514.                 best_guess_rating = guess_rating;
  515.                 best_matches_hints = true;
  516.             }
  517.         }
  518.     } else {
  519.         // Sparse case - use map
  520.         map<hint, int> hint_class_counts;
  521.         for (int i = 0; i < guess_words.size(); i++) {
  522.             hint_class_counts.clear();
  523.             bool skip_to_next_word = false;
  524.             for (int j = 0; j < valid_words.size(); j++) {
  525.                 int idx = get_wordle_hints(guess_words[i], valid_words[j]);
  526.                 hint_class_counts[idx]++;
  527.                 // if (hint_class_counts[idx] > best_guess_rating) {
  528.                 //     skip_to_next_word = true;
  529.                 //     break;
  530.                 // }
  531.             }
  532.             if (skip_to_next_word) {
  533.                 continue;
  534.             }
  535.  
  536.             int guess_rating = 0;
  537.             // Rating for this guess is max of all hint class counts
  538.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  539.                 if (iter->second >= guess_rating) {
  540.                     guess_rating = iter->second;
  541.                 }
  542.             }
  543.  
  544.             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())) {
  545.                 best_guess = guess_words[i];
  546.                 best_guess_rating = guess_rating;
  547.                 best_matches_hints = matches_hints(guess_words[i], guesses_so_far);
  548.             } 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())) {
  549.                 // Among the best candidates, prioritize those that are valid words satisfying all the hints
  550.                 // so far, so that we have a chance to guess the word.
  551.                 best_guess = guess_words[i];
  552.                 best_guess_rating = guess_rating;
  553.                 best_matches_hints = true;
  554.             }
  555.         }
  556.     }
  557.  
  558.     result_is_correct_word = false;
  559.     rating = best_guess_rating;
  560.     return best_guess;
  561. }
  562.  
  563. bool can_guess_word_max_depth(const vector<char*>& valid_words, char* word, const vector<guess>& guesses_so_far, int max_depth) {
  564.     if (valid_words.size() <= 1) {
  565.         return true;
  566.     }
  567.     if (max_depth == 0) {
  568.         return false;
  569.     }
  570.  
  571.     for (int i = 0; i < guess_words.size(); i++) {
  572.         if (is_among_guesses(guess_words[i], guesses_so_far)) {
  573.             continue;
  574.         }
  575.         vector<guess> guesses_so_far2 = guesses_so_far;
  576.         guesses_so_far2.push_back(guess(guess_words[i], get_wordle_hints(guess_words[i], word)));
  577.         vector<char*> valid_words2 = get_valid_words(valid_words, guesses_so_far2);
  578.         if (valid_words2.size() == valid_words.size()) {
  579.             continue; // No new information
  580.         }
  581.  
  582.         if (!can_guess_word_max_depth(valid_words2, word, guesses_so_far2, max_depth - 1)) {
  583.             return false;
  584.         }
  585.     }
  586.  
  587.     return true;
  588. }
  589.  
  590. void cache_best_first_guess() {
  591.     if (best_first_guess == NULL) {
  592.         vector<guess> guesses_so_far;
  593.         bool result_is_correct_word;
  594.         int rating;
  595.         cout << "Computing and caching best first guess..." << endl;
  596.         auto start = high_resolution_clock::now();
  597.         best_first_guess = get_best_guess(answer_words, result_is_correct_word, guesses_so_far, rating);
  598.         auto stop = high_resolution_clock::now();
  599.         cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  600.         // best_first_guess = tokenized_words["SNARE"];
  601.     }
  602. }
  603.  
  604. struct pairi {
  605.     int i;
  606.     int j;
  607.  
  608.     bool operator<(const pairi p) const {
  609.         if (i < p.i)
  610.             return true;
  611.         else if (i > p.i)
  612.             return false;
  613.         else
  614.             return j < p.j;
  615.     }
  616. };
  617.  
  618. int thread_num;
  619. int num_threads;
  620.  
  621. void best_first_k() {
  622.     map<hint, int> hint_class_counts;
  623.     int best_worst_count_so_far = 7;
  624.     // int best_worst_count_so_far = 6; // NOLES CRAFT WIMPY
  625.     int best_pair_i = -1, best_pair_j = -1, best_pair_k = -1;
  626.  
  627.     char buf[300];
  628.     sprintf(buf, "results.small.txt", thread_num);
  629.     ofstream of(buf);
  630.     cout << "thread_num: " << thread_num << endl;
  631.     for (int i=thread_num; i < guess_words.size(); i+= num_threads) {
  632.         cout << "i: " << i << " " << guess_words[i] << endl;
  633.         for (int j=i+1; j < guess_words.size(); j++) {
  634.             if ((j % 1000) == 0) {
  635.                 cout << "i: " << i << " " << guess_words[i] << " j: " << j << " " << guess_words[j] << endl;
  636.             }
  637.  
  638.             hint_class_counts.clear();
  639.             for (int m = 0; m < answer_words.size(); m++) {
  640.                 int combined_hint = get_wordle_hints(guess_words[i], answer_words[m]) * num_hint_values +
  641.                     get_wordle_hints(guess_words[j], answer_words[m]);
  642.                 int count = hint_class_counts[combined_hint];
  643.                 hint_class_counts[combined_hint] = count + 1;
  644.             }
  645.  
  646.             int guess_rating = 0;
  647.             vector<guess> guesses_so_far;
  648.             for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  649.                 if (iter->second >= guess_rating) {
  650.                     guess_rating = iter->second;
  651.                     guesses_so_far.clear();
  652.                     guesses_so_far.push_back(guess(guess_words[i], iter->first / num_hint_values));
  653.                     guesses_so_far.push_back(guess(guess_words[j], iter->first % num_hint_values));
  654.                 }
  655.             }
  656.             vector<char*> valid_words_first_2 = get_valid_words(answer_words, guesses_so_far);
  657.  
  658.             for (int k = j + 1; k < guess_words.size(); k++) {
  659.                 hint_class_counts.clear();
  660.  
  661.                 bool exceeded_count = false;
  662.                 for (int m = 0; m < valid_words_first_2.size(); m++) {
  663.                     int combined_hint = get_wordle_hints(guess_words[i], valid_words_first_2[m]) * num_hint_values * num_hint_values +
  664.                         get_wordle_hints(guess_words[j], valid_words_first_2[m]) * num_hint_values +
  665.                         get_wordle_hints(guess_words[k], valid_words_first_2[m]);
  666.                     int count = hint_class_counts[combined_hint];
  667.                     hint_class_counts[combined_hint] = count + 1;
  668.                     if (count + 1 > best_worst_count_so_far) {
  669.                         exceeded_count = true;
  670.                         break;
  671.                     }
  672.                 }
  673.                 if (exceeded_count) {
  674.                     continue;
  675.                 }
  676.  
  677.                 for (int m = 0; m < answer_words.size(); m++) {
  678.                     int combined_hint = get_wordle_hints(guess_words[i], answer_words[m]) * num_hint_values * num_hint_values +
  679.                         get_wordle_hints(guess_words[j], answer_words[m]) * num_hint_values +
  680.                         get_wordle_hints(guess_words[k], answer_words[m]);
  681.                     int count = hint_class_counts[combined_hint];
  682.                     hint_class_counts[combined_hint] = count + 1;
  683.                     if (count + 1 > best_worst_count_so_far) {
  684.                         break;
  685.                     }
  686.                 }
  687.  
  688.                 int guess_rating = 0;
  689.                 for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  690.                     if (iter->second >= guess_rating) {
  691.                         guess_rating = iter->second;
  692.                     }
  693.                 }
  694.                 if (guess_rating < best_worst_count_so_far) {
  695.                     best_worst_count_so_far = guess_rating;
  696.                     best_pair_i = i;
  697.                     best_pair_j = j;
  698.                     best_pair_k = k;
  699.                     cout << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  700.                     of << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  701.                     of.flush();
  702.                 } else if (guess_rating == best_worst_count_so_far) {
  703.                     cout << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  704.                     of << guess_words[i] << " " << guess_words[j] << " " << guess_words[k] << " " << best_worst_count_so_far << endl;
  705.                     of.flush();
  706.                 }
  707.             }
  708.         }
  709.     }
  710.  
  711.     cout << guess_words[best_pair_i] << " " << guess_words[best_pair_j] << " " << guess_words[best_pair_k] << " " << best_worst_count_so_far << endl;
  712.     of << guess_words[best_pair_i] << " " << guess_words[best_pair_j] << " " << guess_words[best_pair_k] << " " << best_worst_count_so_far << endl;
  713.     of.flush();
  714. }
  715.  
  716. void print_words(const vector<char*>& valid_words);
  717.  
  718. void best_4th() {
  719.     ifstream in("solutions6_minus.txt");
  720.     ofstream of("results.4th.txt");
  721.  
  722.     while (true) {
  723.         map<hint, int> hint_class_counts;
  724.         // int best_worst_count_so_far = 2;
  725.         // int best_worst_count_so_far = 6; // NOLES CRAFT WIMPY
  726.         int best_pair_i, best_pair_j = -1;
  727.  
  728.         cout << "Precomputing best first guess..." << endl;
  729.         cache_best_first_guess();
  730.         char* first_guess = best_first_guess;
  731. #if 1
  732.         string line;
  733.         getline(in, line);
  734.         if (in.eof()) break;
  735.         if (line.length() < word_length * 3 + 2) {
  736.             continue;
  737.         }
  738.         char* first_word = _strdup(line.c_str());
  739.         first_word[word_length] = '\0';
  740.         char* second_word = _strdup(line.c_str() + word_length + 1);
  741.         second_word[word_length] = '\0';
  742.         char* third_word = _strdup(line.c_str() + word_length + 1 + word_length + 1);
  743.         third_word[word_length] = '\0';
  744.         // cout << "First guess: " << first_guess << endl;
  745.         // first_guess = _strdup("TAILS");
  746. #else
  747.         char* first_word = _strdup("CHANT");
  748.         char* second_word = _strdup("SORED");
  749.         char* third_word = _strdup("BLIMP");
  750.         // char* forth_word = _strdup("FUGLY");
  751.         // EVOKE
  752. #endif
  753.         cout << "First three guesses: " << first_word << " " << second_word << " " << third_word << endl;
  754.  
  755.         hint_class_counts.clear();
  756.         for (int m = 0; m < answer_words.size(); m++) {
  757.             hint combined_hint = get_wordle_hints(first_word, answer_words[m]) * num_hint_values * num_hint_values +
  758.                 get_wordle_hints(second_word, answer_words[m]) * num_hint_values +
  759.                 get_wordle_hints(third_word, answer_words[m]);
  760.             int count = hint_class_counts[combined_hint];
  761.             hint_class_counts[combined_hint] = count + 1;
  762.         }
  763.  
  764.         // We want the word list sorted with largest classes first, this will give us the fastest break out
  765.         std::vector<std::pair<hint, int>> pairs;
  766.         for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); ++iter) {
  767.             pairs.push_back(*iter);
  768.         }
  769.         sort(pairs.begin(), pairs.end(), [=](std::pair<hint, int>& a, std::pair<hint, int>& b) {
  770.             return a.second > b.second;
  771.         });
  772.  
  773.         int guess_rating = 0;
  774.         vector<char*> valid_words_worst_class;
  775.         vector<char*> answer_words_minus_singletons;
  776.         vector<guess> guesses_so_far;
  777.         vector<guess> guesses_so_far_worst;
  778.         for (auto iter = pairs.begin(); iter != pairs.end(); iter++) {
  779.             cout << iter->second << " ";
  780.             if (iter->second <= 1) {
  781.                 break;
  782.             }
  783.             guesses_so_far.clear();
  784.             guesses_so_far.push_back(guess(first_word, iter->first / num_hint_values / num_hint_values));
  785.             guesses_so_far.push_back(guess(second_word, (iter->first / num_hint_values) % num_hint_values));
  786.             guesses_so_far.push_back(guess(third_word, iter->first % num_hint_values));
  787.             vector<char*> valid_words_this_class = get_valid_words(answer_words, guesses_so_far);
  788.             for (int m = 0; m < valid_words_this_class.size(); m++) {
  789.                 answer_words_minus_singletons.push_back(valid_words_this_class[m]);
  790.             }
  791.         }
  792.         cout << endl;
  793.         cout << "Singletons removed: " << (answer_words.size() - answer_words_minus_singletons.size()) << endl;
  794.  
  795.         for (int i = 0; i < guess_words.size(); i++) {
  796.             for (int j = i + 1; j < guess_words.size(); j++) {
  797.                 hint_class_counts.clear();
  798.                 bool skip = false;
  799.                 for (int m = 0; m < answer_words_minus_singletons.size(); m++) {
  800.                     hint combined_hint = get_wordle_hints(guess_words[i], answer_words_minus_singletons[m]) * num_hint_values * num_hint_values * num_hint_values * num_hint_values +
  801.                         get_wordle_hints(guess_words[j], answer_words_minus_singletons[m]) * num_hint_values * num_hint_values * num_hint_values +
  802.                         get_wordle_hints(first_word, answer_words_minus_singletons[m]) * num_hint_values * num_hint_values +
  803.                         get_wordle_hints(second_word, answer_words_minus_singletons[m]) * num_hint_values +
  804.                         get_wordle_hints(third_word, answer_words_minus_singletons[m]);
  805.                     int count = hint_class_counts[combined_hint];
  806.                     hint_class_counts[combined_hint] = count + 1;
  807.                     if (count + 1 > 2) {
  808.                         skip = true;
  809.                         break;
  810.                     }
  811.                 }
  812.                 if (skip) {
  813.                     continue;
  814.                 }
  815.  
  816.                 int guess_rating = 0;
  817.                 for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  818.                     if (iter->second >= guess_rating) {
  819.                         guess_rating = iter->second;
  820.                     }
  821.                 }
  822.                 if (guess_rating <= 2) {
  823.                     cout << first_word << " " << second_word << " " << third_word << " " << guess_words[i] << " " << guess_words[j] << " " << guess_rating << endl;
  824.                     of << first_word << " " << second_word << " " << third_word << " " << guess_words[i] << " " << guess_words[j] << " " << guess_rating << endl;
  825.  
  826.                     std::vector<std::pair<hint, int>> pairs;
  827.                     for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); ++iter) {
  828.                         pairs.push_back(*iter);
  829.                     }
  830.                     sort(pairs.begin(), pairs.end(), [=](std::pair<hint, int>& a, std::pair<hint, int>& b) {
  831.                         return a.second > b.second;
  832.                     });
  833.  
  834.                     for (auto iter = pairs.begin(); iter != pairs.end(); iter++) {
  835.                         cout << iter->second << " ";
  836.                         if (iter->second <= 1) {
  837.                             break;
  838.                         }
  839.                         guesses_so_far.clear();
  840.                         hint combined_hint = iter->first;
  841.                         guesses_so_far.push_back(guess(third_word, combined_hint % num_hint_values));
  842.                         combined_hint /= num_hint_values;
  843.                         guesses_so_far.push_back(guess(second_word, combined_hint % num_hint_values));
  844.                         combined_hint /= num_hint_values;
  845.                         guesses_so_far.push_back(guess(first_word, combined_hint % num_hint_values));
  846.                         combined_hint /= num_hint_values;
  847.                         guesses_so_far.push_back(guess(guess_words[j], combined_hint % num_hint_values));
  848.                         combined_hint /= num_hint_values;
  849.                         guesses_so_far.push_back(guess(guess_words[i], combined_hint % num_hint_values));
  850.                         vector<char*> valid_words_this_class = get_valid_words(answer_words, guesses_so_far);
  851.                         cout << "[";
  852.                         for (int m = 0; m < valid_words_this_class.size(); m++) {
  853.                             cout << valid_words_this_class[m];
  854.                             if (m != valid_words_this_class.size() - 1) {
  855.                                 cout << ",";
  856.                             }
  857.                         }
  858.                         cout << "] ";
  859.                     }
  860.                     cout << endl;
  861.                 }
  862.             }
  863.         }
  864.     }
  865. }
  866.  
  867. void find_best_first_guess() {
  868.     cache_best_first_guess();
  869.     cout << "Best first guess: " << best_first_guess << endl;
  870. }
  871.  
  872. void print_words(const vector<char*>& valid_words) {
  873.     cout << "[";
  874.     if (valid_words.size() <= 50) {
  875.         int limit = valid_words.size() <= 50 ? valid_words.size() : 20;
  876.         for (int i = 0; i < limit; i++) {
  877.             cout << valid_words[i] << (i < valid_words.size() - 1 ? ", " : "");
  878.         }
  879.         if (limit < valid_words.size()) {
  880.             cout << "(" << (valid_words.size() - limit) << " other words" << ")";
  881.         }
  882.     } else {
  883.         cout << "(" << valid_words.size() << " words)" << endl;
  884.     }
  885.     cout << "]";
  886. }
  887.  
  888. void guess_unknown_word() {
  889.     vector<guess> guesses_so_far;
  890.     bool result_is_correct_word;
  891.     int rating;
  892.  
  893.     string hint_string;
  894.     char* current_guess;
  895.     do {
  896.         cache_best_first_guess();
  897.         current_guess = best_first_guess;
  898.         cout << "Next guess: " << current_guess << endl;
  899.         cout << "Enter result (k for black, y for yellow, g for green, or ENTER if word is not accepted): ";
  900.         getline(cin, hint_string);
  901.         if (hint_string.length() == 0) {
  902.             strcpy(best_first_guess, ""); // Remove from dictionary
  903.             best_first_guess = NULL;
  904.         }
  905.     } while (hint_string.length() == 0);
  906.     guesses_so_far.push_back(guess(current_guess, string_to_hint(hint_string)));
  907.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  908.     print_words(valid_words);
  909.     cout << endl << endl;
  910.  
  911.     while (true) {
  912.         char* current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  913.         if (current_guess == NULL) {
  914.             cout << "Word is not in dictionary." << endl;
  915.             break;
  916.         } else if (result_is_correct_word) {
  917.             cout << "The word is: " << current_guess << endl;
  918.             break;
  919.         }
  920.         cout << "Next guess: " << current_guess << endl;
  921.         cout << "Enter result (k for black, y for yellow, g for green, or ENTER if word is not accepted): ";
  922.         getline(cin, hint_string);
  923.         if (hint_string.length() == 0) {
  924.             strcpy(current_guess, ""); // Remove from dictionary
  925.             continue;
  926.         }
  927.         guesses_so_far.push_back(guess(current_guess, string_to_hint(hint_string)));
  928.         valid_words = get_valid_words(valid_words, guesses_so_far);
  929.         print_words(valid_words);
  930.         cout << endl << endl;
  931.     }
  932. }
  933.  
  934. void solve_single_word_helper(char* answer) {
  935.     vector<guess> guesses_so_far;
  936.     bool result_is_correct_word;
  937.     int rating;
  938.  
  939.     auto start = high_resolution_clock::now();
  940.     cache_best_first_guess();
  941.     char* current_guess = best_first_guess;
  942.     hint hint = get_wordle_hints(current_guess, answer);
  943.     guesses_so_far.push_back(guess(current_guess, hint));
  944.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  945.     cout << current_guess << " " << hint_to_string(hint) << " ";
  946.     print_words(valid_words);
  947.     cout << endl << endl;
  948.  
  949.     while (strcmp(current_guess, answer) != 0) {
  950.         current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  951.         if (current_guess == NULL) {
  952.             cout << "Word is not in dictionary. You may have typed it incorrectly." << endl;
  953.             break;
  954.         }
  955.         hint = get_wordle_hints(current_guess, answer);
  956.         guesses_so_far.push_back(guess(current_guess, hint));
  957.         valid_words = get_valid_words(valid_words, guesses_so_far);
  958.         cout << current_guess << " " << hint_to_string(hint) << " ";
  959.         print_words(valid_words);
  960.         cout << endl << endl;
  961.     }
  962.     auto stop = high_resolution_clock::now();
  963.     cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  964.     cout << "Number of guesses: " << guesses_so_far.size() << endl;
  965. }
  966.  
  967. void solve_single_word() {
  968.     char* answer = NULL;
  969.     do {
  970.         cout << "Enter solution word: ";
  971.         string answer_str;
  972.         getline(cin, answer_str);
  973.         answer = _strdup(answer_str.c_str());
  974.         toupper_cstr(answer);
  975.         if (strlen(answer) != word_length) {
  976.             cout << "Word is wrong length, must be " << word_length << " letters." << endl;
  977.             continue;
  978.         }
  979.         if (tokenized_words.find(answer) == tokenized_words.end()) {
  980.             cout << "Word is not in guess list or solutions list." << endl;
  981.             continue;
  982.         }
  983.         answer = tokenized_words[answer];
  984.         break;
  985.     } while (true);
  986.  
  987.     solve_single_word_helper(answer);
  988. }
  989.  
  990. void solve_all_words_in_dictionary() {
  991.     vector<guess> guesses_so_far;
  992.     bool result_is_correct_word;
  993.     int rating;
  994.     double best_average = 100000.0f;
  995.  
  996.     auto overall_start = high_resolution_clock::now();
  997.  
  998.     char* best_first_word_solve_all = NULL;
  999.     char* best_second_word_solve_all = NULL;
  1000.     char* best_third_word_solve_all = NULL;
  1001.  
  1002.     // ifstream in("solutions6.txt");
  1003.  
  1004.     while (true) {
  1005.         cout << "Precomputing best first guess..." << endl;
  1006.         cache_best_first_guess();
  1007.         char* first_guess = best_first_guess;
  1008. #if false
  1009.         string line;
  1010.         getline(in, line);
  1011.         if (in.eof()) break;
  1012.         if (line.length() < word_length * 3 + 2) {
  1013.             continue;
  1014.         }
  1015.         char* first_guess = _strdup(line.c_str());
  1016.         first_guess[word_length] = '\0';
  1017.         char* second_guess = _strdup(line.c_str() + word_length + 1);
  1018.         second_guess[word_length] = '\0';
  1019.         char* third_guess = _strdup(line.c_str() + word_length + 1 + word_length + 1);
  1020.         third_guess[word_length] = '\0';
  1021.         // cout << "First guess: " << first_guess << endl;
  1022.         first_guess = _strdup("CHANT");
  1023.         second_guess = _strdup("SORED");
  1024.         third_guess = _strdup("BLIMP");
  1025.         cout << "First three guesses: " << first_guess << " " << second_guess << " " << third_guess << endl;
  1026.         // first_guess = _strdup("TAILS");
  1027. #endif
  1028.  
  1029. #if 0
  1030.         {
  1031.             vector<guess> guesses_so_far_first;
  1032.             char* foo;
  1033.             foo = _strdup("-----");
  1034.             for (int i = 0; i < word_length; i++) {
  1035.                 for (int j = i + 1; j < word_length; j++) {
  1036.                     foo[0] = first_guess[i];
  1037.                     foo[1] = first_guess[j];
  1038.                     guesses_so_far_first.clear();
  1039.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("yykkk")));
  1040.                     cout << " " << first_guess[i] << " " << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  1041.                     guesses_so_far_first.clear();
  1042.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("ykkkk")));
  1043.                     cout << " " << first_guess[i] << "!" << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  1044.                     guesses_so_far_first.clear();
  1045.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("kykkk")));
  1046.                     cout << "!" << first_guess[i] << " " << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  1047.                     guesses_so_far_first.clear();
  1048.                     guesses_so_far_first.push_back(guess(foo, string_to_hint("kkkkk")));
  1049.                     cout << "!" << first_guess[i] << "!" << first_guess[j] << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  1050.                     cout << endl;
  1051.                 }
  1052.             }
  1053.         }
  1054.  
  1055.         {
  1056.             vector<guess> guesses_so_far_first;
  1057.             char* foo;
  1058.             foo = _strdup("-----");
  1059.             char* bar;
  1060.             bar = _strdup("kkkkk");
  1061.             for (int i = 0; i < word_length; i++) {
  1062.                 for (int j = 0; j < word_length; j++) {
  1063.                     guesses_so_far_first.clear();
  1064.                     foo[j] = first_guess[i];
  1065.                     bar[j] = 'g';
  1066.                     guesses_so_far_first.push_back(guess(foo, string_to_hint(bar)));
  1067.                     cout << first_guess[i] << " " << j << " " << get_valid_words(answer_words, guesses_so_far_first).size() << endl;
  1068.                     bar[j] = 'k';
  1069.                     foo[j] = '-';
  1070.                 }
  1071.             }
  1072.         }
  1073. #endif
  1074.  
  1075.         char** second_guesses = NULL;
  1076.         int empty_classes = 0;
  1077. #if 1
  1078.         if (answer_words.size() * 1000 > num_hint_values) {
  1079.             cout << "Precomputing best second guesses..." << endl;
  1080.             auto start = high_resolution_clock::now();
  1081.             second_guesses = new char* [num_hint_values];
  1082.             for (int i = 0; i < num_hint_values; i++) {
  1083.                 vector<guess> guesses_so_far;
  1084.                 guesses_so_far.push_back(guess(first_guess, i));
  1085.                 vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  1086.                 second_guesses[i] = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  1087.                 // second_guesses[i] = _strdup("DECOR");
  1088. #if 0
  1089.                 cout << hint_to_string(i) << " " << valid_words.size() << " " << (second_guesses[i] == NULL ? "" : second_guesses[i]) << endl;
  1090. #endif
  1091.                 if (valid_words.size() == 0) {
  1092.                     empty_classes++;
  1093.                 }
  1094.                 // print_words(valid_words);
  1095.                 // cout << endl;
  1096.  
  1097.                 // if ((i % 1000) == 0) {
  1098.                 //     auto stop = high_resolution_clock::now();
  1099.                 //     cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * num_hint_values << " s" << endl;
  1100.                 // }
  1101.             }
  1102.             auto stop = high_resolution_clock::now();
  1103.             cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  1104.             cout << "Empty hint classes: " << empty_classes << endl;
  1105.         }
  1106. #endif
  1107.  
  1108.         char*** third_guesses = NULL;
  1109. #if 1
  1110.         if (sqrt(answer_words.size() * 1000) > num_hint_values) {
  1111.             cout << "Precomputing third guesses..." << endl;
  1112.             auto start = high_resolution_clock::now();
  1113.             third_guesses = new char** [num_hint_values * num_hint_values];
  1114.             for (int i = 0; i < num_hint_values; i++) {
  1115.                 vector<guess> guesses_so_far_first;
  1116.                 guesses_so_far_first.push_back(guess(first_guess, i));
  1117.                 vector<char*> valid_words_first = get_valid_words(answer_words, guesses_so_far_first);
  1118.  
  1119.                 third_guesses[i] = new char* [num_hint_values];
  1120.                 for (int j = 0; j < num_hint_values; j++) {
  1121.                     vector<guess> guesses_so_far;
  1122.                     guesses_so_far.push_back(guess(first_guess, i));
  1123.                     guesses_so_far.push_back(guess(second_guesses[i], j));
  1124.                     // guesses_so_far.push_back(guess(second_guess, i));
  1125.                     vector<char*> valid_words = get_valid_words(valid_words_first, guesses_so_far);
  1126.                     third_guesses[i][j] = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  1127.  
  1128. #if 0
  1129.                     if (valid_words.size() > 10) {
  1130.                         cout << hint_to_string(i) << " " << (second_guesses[i] == NULL ? "null" : second_guesses[i]) << " " << hint_to_string(j) << " " << valid_words.size() << endl;
  1131.                     }
  1132. #endif
  1133.                 }
  1134.                 // if ((i % 1000) == 0) {
  1135.                 //     auto stop = high_resolution_clock::now();
  1136.                 //     cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * num_hint_values << " s" << endl;
  1137.                 // }
  1138.             }
  1139.             auto stop = high_resolution_clock::now();
  1140.             cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  1141.         }
  1142. #endif
  1143.  
  1144.         int max_guesses = 0;
  1145.         char* worst_word = NULL;
  1146.  
  1147.         cout << "Solving all words in dictionary..." << endl;
  1148.         auto start = high_resolution_clock::now();
  1149.         int histogram[50] = { 0 };
  1150.         for (int i = 0; i < answer_words.size(); i++) {
  1151.             if ((i % 1000) == 0) {
  1152.                 cout << i << endl;
  1153.                 // auto stop = high_resolution_clock::now();
  1154.                 // cout << "Projected time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 / i * answer_words.size() << " s" << endl;
  1155.             }
  1156.             vector<guess> guesses_so_far;
  1157.             char* current_guess = first_guess;
  1158.  
  1159.             guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  1160.  
  1161.             int number_of_guesses = 1;
  1162.             hint first_hint;
  1163.             vector<char*> valid_words;
  1164.  
  1165.             if (strcmp(current_guess, answer_words[i]) != 0) {
  1166.                 first_hint = get_wordle_hints(current_guess, answer_words[i]);
  1167.                 if (second_guesses != NULL) {
  1168.                     current_guess = second_guesses[first_hint];
  1169.                 } else {
  1170.                     current_guess = get_best_guess(answer_words, result_is_correct_word, guesses_so_far, rating);
  1171.                 }
  1172.                 // current_guess = second_guess;
  1173.                 number_of_guesses++;
  1174.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  1175.                 valid_words = get_valid_words(answer_words, guesses_so_far);
  1176.             }
  1177.  
  1178.             if (/*third_guesses != NULL &&*/ strcmp(current_guess, answer_words[i]) != 0) {
  1179.                 current_guess = third_guesses[first_hint][get_wordle_hints(current_guess, answer_words[i])];
  1180.                 // current_guess = third_guess;
  1181.                 number_of_guesses++;
  1182.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  1183.                 valid_words = get_valid_words(valid_words, guesses_so_far);
  1184.             }
  1185.  
  1186.             while (strcmp(current_guess, answer_words[i]) != 0) {
  1187.                 // if (guesses_so_far.size() == 3) {
  1188.                 //     current_guess = forth_word;
  1189.                 // } else {
  1190.                     current_guess = get_best_guess(valid_words, result_is_correct_word, guesses_so_far, rating);
  1191.                 // }
  1192.                 number_of_guesses++;
  1193.                 guesses_so_far.push_back(guess(current_guess, get_wordle_hints(current_guess, answer_words[i])));
  1194.                 valid_words = get_valid_words(valid_words, guesses_so_far);
  1195.             }
  1196.  
  1197.             histogram[number_of_guesses]++;
  1198.             if (number_of_guesses > max_guesses) {
  1199.                 worst_word = answer_words[i];
  1200.                 max_guesses = number_of_guesses;
  1201.             }
  1202.  
  1203. #if 0
  1204.             if (number_of_guesses == 5) {
  1205.                 cout << "    " << answer_words[i] << ": ";
  1206.                 for (int i = 0; i < guesses_so_far.size(); i++) {
  1207.                     cout << guesses_so_far[i].word << " " << hint_to_string(guesses_so_far[i].hint) << "  ";
  1208.                 }
  1209.                 cout << endl;
  1210.             }
  1211. #endif
  1212.         }
  1213.         auto stop = high_resolution_clock::now();
  1214.         cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  1215.  
  1216.         auto overall_stop = high_resolution_clock::now();
  1217.         cout << "Total time: " << duration_cast<microseconds>(overall_stop - overall_start).count() / 1000000.0 << " s" << endl;
  1218.         cout << "Time per word: " << duration_cast<microseconds>(overall_stop - overall_start).count() / 1000.0 / answer_words.size() << " ms" << endl;
  1219.  
  1220.         int total_count = 0, total_guesses = 0;
  1221.         for (int i = 0; i < sizeof(histogram) / sizeof(histogram[0]); i++) {
  1222.             if (histogram[i] != 0) {
  1223.                 cout << i << ": " << histogram[i] << endl;
  1224.                 total_count += histogram[i];
  1225.                 total_guesses += i * histogram[i];
  1226.             }
  1227.         }
  1228.         double average = double(total_guesses) / total_count;
  1229.         cout << "Average: " << average << " guesses" << endl;
  1230.         cout << "Worst word was " << worst_word << ":" << endl;
  1231.         // solve_single_word_helper(worst_word);
  1232.  
  1233.         if (average < best_average) {
  1234.             best_first_word_solve_all = first_guess;
  1235.             // best_second_word_solve_all = second_guess;
  1236.             // best_third_word_solve_all = third_guess;
  1237.             // best_forth_word = forth_word;
  1238.             best_average = average;
  1239.         }
  1240.         // 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;
  1241.         // cout << "Best fourth word so far: " << best_forth_word << " with average of " << best_average << endl;
  1242.  
  1243.         // forbid_first_guess(first_guess);
  1244.         break;
  1245.     }
  1246. }
  1247.  
  1248. void show_largest_hint_class() {
  1249.     cout << "Enter three words:" << endl;
  1250.     vector<char*> words;
  1251.     for (int i = 0; i < 3; i++) {
  1252.         string word;
  1253.         cout << "> ";
  1254.         getline(cin, word);
  1255.         for (int i = 0; i < guess_words.size(); i++) {
  1256.             if (strcmp(guess_words[i], word.c_str()) == 0) {
  1257.                 words.push_back(guess_words[i]);
  1258.             }
  1259.         }
  1260.     }
  1261.  
  1262.     map<hint, int> hint_class_counts;
  1263.     // int best_worst_count_so_far = 8;
  1264.     int best_worst_count_so_far = 6; // NOLES CRAFT WIMPY
  1265.     int best_pair_i = -1, best_pair_j = -1, best_pair_k = -1;
  1266.  
  1267.     for (int m = 0; m < answer_words.size(); m++) {
  1268.         int combined_hint = 0;
  1269.         for (int i = words.size() - 1; i >= 0; i--) {
  1270.             combined_hint *= num_hint_values;
  1271.             combined_hint += get_wordle_hints(words[i], answer_words[m]);
  1272.         }
  1273.         hint_class_counts[combined_hint]++;
  1274.     }
  1275.  
  1276.     int guess_rating = 0;
  1277.     vector<guess> guesses_so_far;
  1278.     for (auto iter = hint_class_counts.begin(); iter != hint_class_counts.end(); iter++) {
  1279.         if (iter->second >= guess_rating) {
  1280.             guess_rating = iter->second;
  1281.             guesses_so_far.clear();
  1282.            
  1283.             hint combined_hint = iter->first;
  1284.             for (int i = 0; i < words.size(); i++) {
  1285.                 guesses_so_far.push_back(guess(words[i], combined_hint % num_hint_values));
  1286.                 combined_hint /= num_hint_values;
  1287.             }
  1288.         }
  1289.     }
  1290.     vector<char*> valid_words = get_valid_words(answer_words, guesses_so_far);
  1291.  
  1292.     for (int i = 0; i < guesses_so_far.size(); i++) {
  1293.         cout << guesses_so_far[i].word << " " << hint_to_string(guesses_so_far[i].hint) << endl;
  1294.     }
  1295.     cout << endl;
  1296.     print_words(valid_words);
  1297.     cout << endl;
  1298. }
  1299.  
  1300. int hint_count_greens(hint h) {
  1301.     int num_greens = 0;
  1302.     for (int i = 0; i < word_length; i++) {
  1303.         if ((h % 3) == 2) {
  1304.             num_greens++;
  1305.         }
  1306.         h /= 3;
  1307.     }
  1308.     return num_greens;
  1309. }
  1310.  
  1311. int sum_sizes(vector<vector<guess>> all_paths) {
  1312.     int result = 0;
  1313.     for (int i = 0; i < all_paths.size(); i++) {
  1314.         result += all_paths[i].size();
  1315.     }
  1316.     return result;
  1317. }
  1318.  
  1319. int hard_mode_get_tree_height(ofstream& of, vector<char*>& valid_guesses, vector<char*>& valid_answers, vector<guess>& guesses_so_far, int depth_limit_min, int depth_limit_max, vector<vector<guess>>& all_paths, int& path_guesses_count, bool collect_paths);
  1320.  
  1321. void hard_mode_process_word(char* guess_word, ofstream& of, vector<char*>& valid_guesses, vector<char*>& valid_answers, vector<guess>& guesses_so_far, int depth_limit_min, int depth_limit_max, int& min_depth, char*& best_guess, char* letters_in_answers, map<string, bool>& guess_classes, vector<vector<guess>>& all_paths, int& path_guesses_count, bool collect_paths) {
  1322.     vector<int> hint_class_counts;
  1323.     hint_class_counts.resize(num_hint_values);
  1324.  
  1325.     bool found_non_answer_letter = false;
  1326.     string guess_class_str(guess_word);
  1327.     for (int cidx = 0; cidx < word_length; cidx++) {
  1328.         if (!letters_in_answers[guess_class_str[cidx]]) {
  1329.             guess_class_str[cidx] = '-';
  1330.             found_non_answer_letter = true;
  1331.         }
  1332.     }
  1333.     if (found_non_answer_letter) {
  1334.         if (guess_classes.find(guess_class_str) != guess_classes.end()) {
  1335.             return; // Duplicate on all letters occuring in answers, can skip
  1336.         }
  1337.         guess_classes[guess_class_str] = true;
  1338.     }
  1339.  
  1340.     int depth_i = 0;
  1341.     int max_count = 0;
  1342.     for (int m = 0; m < valid_answers.size(); m++) {
  1343.         hint hint = get_wordle_hints(guess_word, valid_answers[m]);
  1344.         if (++hint_class_counts[hint] > max_count) {
  1345.             max_count = hint_class_counts[hint];
  1346.         }
  1347.     }
  1348.  
  1349.     vector<vector<guess>> all_paths_this_word;
  1350.     int path_guesses_count_this_word = 0;
  1351.  
  1352.     if (max_count <= 2) {
  1353.         depth_i = 1 + max_count;
  1354.  
  1355.         if (collect_paths)
  1356.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  1357.             if (hint_class_counts[h] == 0) {
  1358.                 // do nothing
  1359.             } else if (h == num_hint_values - 1 /* all green */) {
  1360.                 guesses_so_far.push_back(guess(guess_word, num_hint_values - 1));
  1361.                 all_paths_this_word.push_back(guesses_so_far);
  1362.                 guesses_so_far.pop_back();
  1363.             } else { // (hint_class_counts[h] <= 2)
  1364.                 guesses_so_far.push_back(guess(guess_word, h));
  1365.                 vector<guess> guesses_so_far_small;
  1366.                 guesses_so_far_small.push_back(guess(guess_word, h));
  1367.                 vector<char*> valid_answers_this_class = get_valid_words(valid_answers, guesses_so_far_small);
  1368.                 for (int i = 0; i < valid_answers_this_class.size(); i++) {
  1369.                     for (int j = 0; j <= i; j++) {
  1370.                         guesses_so_far.push_back(guess(valid_answers_this_class[j], get_wordle_hints(valid_answers_this_class[j], valid_answers_this_class[i])));
  1371.                     }
  1372.                     all_paths_this_word.push_back(guesses_so_far);
  1373.                     for (int j = 0; j <= i; j++) {
  1374.                         guesses_so_far.pop_back();
  1375.                     }
  1376.                 }
  1377.                 guesses_so_far.pop_back();
  1378.             }
  1379.         }
  1380.  
  1381.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  1382.             if (hint_class_counts[h] == 0) {
  1383.                 // do nothing
  1384.             } else if (h == num_hint_values - 1 /* all green */) {
  1385.                 path_guesses_count_this_word += guesses_so_far.size() + 1;
  1386.             } else { // (hint_class_counts[h] <= 2)
  1387.                 for (int i = 1; i <= hint_class_counts[h]; i++) {
  1388.                     path_guesses_count_this_word += guesses_so_far.size() + 1 + i;
  1389.                 }
  1390.             }
  1391.         }
  1392.         if (collect_paths && path_guesses_count_this_word != sum_sizes(all_paths_this_word)) {
  1393.             cout << "BAD" << endl;
  1394.         }
  1395.     } else {
  1396.         int non_empty_classes = 0;
  1397.         for (hint h = 0; h < num_hint_values; h++) {
  1398.             if (hint_class_counts[h] > 0) {
  1399.                 non_empty_classes++;
  1400.             }
  1401.         }
  1402.         if (non_empty_classes == 1) {
  1403.             // This hint gives no new information, don't bother guessing this word
  1404.             return;
  1405.         }
  1406.  
  1407.         if (guesses_so_far.size() <= 0) {
  1408.             cout << "Starting L" << guesses_so_far.size() << ": ";
  1409.             for (int g = 0; g < guesses_so_far.size(); g++) {
  1410.                 cout << guesses_so_far[g].word << " " << hint_to_string(guesses_so_far[g].hint) << " ";
  1411.             }
  1412.             cout << guess_word << endl;
  1413.         }
  1414.  
  1415.         // Count backwards to encounter most constraining classes first, watch out for unsigned overflow
  1416.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  1417.             if (hint_class_counts[h] == 0) {
  1418.                 continue;
  1419.             } else if (h == num_hint_values - 1 /* all green */) {
  1420.                 if (collect_paths) {
  1421.                     guesses_so_far.push_back(guess(guess_word, num_hint_values - 1));
  1422.                     all_paths_this_word.push_back(guesses_so_far);
  1423.                     guesses_so_far.pop_back();
  1424.                 }
  1425.                 path_guesses_count_this_word += guesses_so_far.size() + 1;
  1426.                 continue;
  1427.             } else if (hint_class_counts[h] <= 2) {
  1428.                 if (collect_paths) {
  1429.                     guesses_so_far.push_back(guess(guess_word, h));
  1430.                     vector<guess> guesses_so_far_small;
  1431.                     guesses_so_far_small.push_back(guess(guess_word, h));
  1432.                     vector<char*> valid_answers_this_class = get_valid_words(valid_answers, guesses_so_far_small);
  1433.                     for (int i = 0; i < valid_answers_this_class.size(); i++) {
  1434.                         for (int j = 0; j <= i; j++) {
  1435.                             guesses_so_far.push_back(guess(valid_answers_this_class[j], get_wordle_hints(valid_answers_this_class[j], valid_answers_this_class[i])));
  1436.                         }
  1437.                         all_paths_this_word.push_back(guesses_so_far);
  1438.                         for (int j = 0; j <= i; j++) {
  1439.                             guesses_so_far.pop_back();
  1440.                         }
  1441.                     }
  1442.                     guesses_so_far.pop_back();
  1443.                 }
  1444.                 for (int i = 1; i <= hint_class_counts[h]; i++) {
  1445.                     path_guesses_count_this_word += guesses_so_far.size() + 1 + i;
  1446.                 }
  1447.                 continue;
  1448.             }
  1449.  
  1450.             int depth_h;
  1451.             if (hint_count_greens(h) == word_length - 1) {
  1452.                 // Trivial case, all but one are green, we must guess all the remaining words one-by-one
  1453.                 depth_h = 1 + hint_class_counts[h];
  1454.  
  1455.                 if (collect_paths) {
  1456.                     guesses_so_far.push_back(guess(guess_word, h));
  1457.                     vector<guess> guesses_so_far_small;
  1458.                     guesses_so_far_small.push_back(guess(guess_word, h));
  1459.                     vector<char*> valid_answers_this_class = get_valid_words(valid_answers, guesses_so_far_small);
  1460.                     for (int i = 0; i < valid_answers_this_class.size(); i++) {
  1461.                         for (int j = 0; j <= i; j++) {
  1462.                             guesses_so_far.push_back(guess(valid_answers_this_class[j], get_wordle_hints(valid_answers_this_class[j], valid_answers_this_class[i])));
  1463.                         }
  1464.                         all_paths_this_word.push_back(guesses_so_far);
  1465.                         for (int j = 0; j <= i; j++) {
  1466.                             guesses_so_far.pop_back();
  1467.                         }
  1468.                     }
  1469.                     guesses_so_far.pop_back();
  1470.                 }
  1471.                 for (int i = 0; i < hint_class_counts[h]; i++) {
  1472.                     path_guesses_count_this_word += guesses_so_far.size() + (1 + i);
  1473.                 }
  1474.             } else {
  1475.                 guesses_so_far.push_back(guess(guess_word, h));
  1476.                 vector<guess> guesses_so_far_small;
  1477.                 guesses_so_far_small.push_back(guess(guess_word, h));
  1478.                 // vector<char*> valid_guesses_this_class;
  1479.                 // if (valid_guesses_sample.size() > 0) {
  1480.                 //     valid_guesses_this_class = get_valid_guesses(valid_guesses_sample, guesses_so_far_small);
  1481.                 // } else {
  1482.                 //     valid_guesses_this_class = get_valid_guesses(valid_guesses, guesses_so_far_small);
  1483.                 // }
  1484.                 vector<char*> valid_answers_this_class = get_valid_words(valid_answers, guesses_so_far_small);
  1485.                 vector<vector<guess>> all_paths_this_class;
  1486.                 int path_guess_counts_this_class = INT_MAX;
  1487.                 depth_h = 1 + hard_mode_get_tree_height(of, /*valid_guesses_this_class*/ valid_guesses, valid_answers_this_class, guesses_so_far,
  1488.                     guesses_so_far.size() <= 1 ? 2 : depth_i - 1, depth_limit_max - 1, all_paths_this_class, path_guess_counts_this_class,
  1489.                     guesses_so_far.size() == 1 ? true : collect_paths);
  1490.                 // if (collect_paths && all_paths_this_class.size() > 0 && all_paths_this_class.size() != valid_answers_this_class.size()) {
  1491.                     // cout << "MISSING WORDS CLASS" << endl;
  1492.                 // }
  1493.                 if (collect_paths) {
  1494.                     for (int z = 0; z < all_paths_this_class.size(); z++) {
  1495.                         all_paths_this_word.push_back(all_paths_this_class[z]);
  1496.                     }
  1497.                 }
  1498.                 path_guesses_count_this_word += path_guess_counts_this_class;
  1499.                 guesses_so_far.pop_back();
  1500.             }
  1501.  
  1502.             if (depth_h > depth_i) {
  1503.                 depth_i = depth_h;
  1504.                 if (depth_i /*>=*/ > min_depth) {
  1505.                     break;
  1506.                 }
  1507.             }
  1508.         }
  1509.     }
  1510.  
  1511.     if (depth_i < min_depth || (depth_i == min_depth && path_guesses_count_this_word < path_guesses_count)) {
  1512.         if ((best_guess != NULL && strcmp(best_guess, "TRYST") == 0) && strcmp(guess_word, "ARISE") == 0) {
  1513.             // cout << "foo" << endl;
  1514.         }
  1515.         best_guess = guess_word;
  1516.         if (collect_paths) {
  1517.             all_paths = all_paths_this_word;
  1518.             // if (all_paths.size() != valid_answers.size()) {
  1519.             //     cout << "MISSING WORDS" << endl;
  1520.             // }
  1521.             // if (collect_paths && path_guesses_count_this_word != sum_sizes(all_paths)) {
  1522.             //     cout << "BAD" << endl;
  1523.             // }
  1524.         }
  1525.         path_guesses_count = path_guesses_count_this_word;
  1526.  
  1527.         if (guesses_so_far.size() > 0) {
  1528.             min_depth = depth_i;
  1529.         }
  1530.         if (guesses_so_far.size() <= 0) {
  1531.             cout << "Best so far: ";
  1532.             of << "Best so far: ";
  1533.             cout << "L" << guesses_so_far.size() << ": ";
  1534.             of << "L" << guesses_so_far.size() << ": ";
  1535.             for (int g = 0; g < guesses_so_far.size(); g++) {
  1536.                 cout << guesses_so_far[g].word << " " << hint_to_string(guesses_so_far[g].hint) << " ";
  1537.                 of << guesses_so_far[g].word << " " << hint_to_string(guesses_so_far[g].hint) << " ";
  1538.             }
  1539.             cout << guess_word << "  " << depth_i << endl;
  1540.             of << guess_word << "  " << depth_i << endl;
  1541.             of.flush();
  1542.         }
  1543.     }
  1544. }
  1545.  
  1546.  
  1547. // If depth is >= depth_limit_max, just return depth_limit_max
  1548. int hard_mode_get_tree_height(ofstream& of, vector<char*>& valid_guesses, vector<char*>& valid_answers, vector<guess>& guesses_so_far, int depth_limit_min, int depth_limit_max, vector<vector<guess>>& all_paths, int& path_guesses_count, bool collect_paths) {
  1549.     if (depth_limit_max <= 1) {
  1550.         path_guesses_count = 1000000;
  1551.         return depth_limit_max;
  1552.     }
  1553.     if (valid_answers.size() <= 2) {
  1554.         if (collect_paths) {
  1555.             for (int i = 0; i < valid_answers.size(); i++) {
  1556.                 for (int j = 0; j <= i; j++) {
  1557.                     guesses_so_far.push_back(guess(valid_answers[j], get_wordle_hints(valid_answers[j], valid_answers[i])));
  1558.                 }
  1559.                 all_paths.push_back(guesses_so_far);
  1560.                 for (int j = 0; j <= i; j++) {
  1561.                     guesses_so_far.pop_back();
  1562.                 }
  1563.             }
  1564.         }
  1565.         path_guesses_count = 0;
  1566.         for (int i = 0; i < valid_answers.size(); i++) {
  1567.             path_guesses_count += guesses_so_far.size() + (1 + i);
  1568.         }
  1569.         return valid_answers.size();
  1570.     }
  1571.     depth_limit_min = max(2, depth_limit_min);
  1572.  
  1573.     int min_depth = depth_limit_max;
  1574.     char* best_guess = NULL;
  1575.  
  1576.     char letters_in_answers[(int)'Z' + 1] = { 0 };
  1577.     for (int i = 0; i < valid_answers.size(); i++) {
  1578.         for (int cidx = 0; cidx < word_length; cidx++) {
  1579.             letters_in_answers[valid_answers[i][cidx]] = 1;
  1580.         }
  1581.     }
  1582.  
  1583.     vector<vector<guess>> all_paths_this_guess;
  1584.  
  1585.     // Before everything try the optimal entropy word, this will help speed up the rest
  1586.     map<string, bool> guess_classes;
  1587.     bool result_is_correct_word;
  1588.     int rating;
  1589.     if (guesses_so_far.size() > 0) {
  1590.         best_first_guess = get_best_guess(valid_answers, result_is_correct_word, guesses_so_far, rating);
  1591.     } else {
  1592.         best_first_guess = tokenized_words["TRACE"];
  1593.     }
  1594.     hard_mode_process_word(best_first_guess, of, valid_guesses, valid_answers, guesses_so_far, depth_limit_min, depth_limit_max, min_depth, best_guess, letters_in_answers, guess_classes, all_paths_this_guess, path_guesses_count, guesses_so_far.size() == 0 ? true : false);
  1595.  
  1596.     // Process valid answers first, this gives us a chance of guessing them prior to having full information,
  1597.     // gives a slightly smaller tree. This will also tend to speed up the search by trying good candidates first.
  1598.     for (int i = 0; min_depth > depth_limit_min && i < valid_answers.size(); i++) {
  1599.         if (strcmp(valid_answers[i], best_first_guess) == 0) continue;
  1600.         hard_mode_process_word(valid_answers[i], of, valid_guesses, valid_answers, guesses_so_far, depth_limit_min, depth_limit_max, min_depth, best_guess, letters_in_answers, guess_classes, all_paths_this_guess, path_guesses_count, guesses_so_far.size() == 0 ? true : false);
  1601.         // if (min_depth <= 2) {
  1602.         //     break;
  1603.         // }
  1604.         if (min_depth < /*<=*/ depth_limit_min || min_depth <= 2) {
  1605.             break;
  1606.         }
  1607.     }
  1608.     for (int i = 0; min_depth > depth_limit_min && i < valid_guesses.size(); i++) {
  1609.         if (strcmp(valid_guesses[i], best_first_guess) == 0) continue;
  1610.         bool found = false;
  1611.         for (int j = 0; j < valid_answers.size(); j++) {
  1612.             if (strcmp(valid_guesses[i], valid_answers[j]) == 0) {
  1613.                 found = true;
  1614.                 break;
  1615.             }
  1616.         }
  1617.         if (found) {
  1618.             continue;
  1619.         }
  1620.  
  1621.         hard_mode_process_word(valid_guesses[i], of, valid_guesses, valid_answers, guesses_so_far, depth_limit_min, depth_limit_max, min_depth, best_guess, letters_in_answers, guess_classes, all_paths_this_guess, path_guesses_count, guesses_so_far.size() == 0 ? true : false);
  1622.  
  1623.         // if (min_depth <= 2) {
  1624.         //     break;
  1625.         // }
  1626.         if (min_depth < /*<=*/ depth_limit_min || min_depth <= 2) {
  1627.             break;
  1628.         }
  1629.     }
  1630.  
  1631.     if (collect_paths && best_guess != NULL) {
  1632.         // Redo this word with collect_paths turned on
  1633.         guess_classes.clear();
  1634.         min_depth++;
  1635.         hard_mode_process_word(best_guess, of, valid_guesses, valid_answers, guesses_so_far, depth_limit_min, depth_limit_max, min_depth, best_guess, letters_in_answers, guess_classes, all_paths_this_guess, path_guesses_count, true);
  1636.         for (int i = 0; i < all_paths_this_guess.size(); i++) {
  1637.             all_paths.push_back(all_paths_this_guess[i]);
  1638.         }
  1639.     }
  1640.  
  1641.     if (guesses_so_far.size() <= 1) {
  1642.         cout << "L" << guesses_so_far.size() << ": ";
  1643.         of << "L" << guesses_so_far.size() << ": ";
  1644.         for (int g = 0; g < guesses_so_far.size(); g++) {
  1645.             cout << guesses_so_far[g].word << " " << hint_to_string(guesses_so_far[g].hint) << " ";
  1646.             of << guesses_so_far[g].word << " " << hint_to_string(guesses_so_far[g].hint) << " ";
  1647.         }
  1648.         cout << (best_guess == NULL ? "(XXX)" : best_guess) << " " << min_depth << " ";
  1649.         of << (best_guess == NULL ? "(XXX)" : best_guess) << " " << min_depth;
  1650.         if (valid_answers.size() <= 10) {
  1651.             print_words(valid_answers);
  1652.         }
  1653.         cout << endl;
  1654.         of << endl;
  1655.  
  1656.         if (collect_paths && guesses_so_far.size() == 1) {
  1657.             for (int i = 0; i < all_paths_this_guess.size(); i++) {
  1658.                 cout << "PATH: ";
  1659.                 of << "PATH: ";
  1660.                 for (int j = 0; j < all_paths_this_guess[i].size(); j++) {
  1661.                     cout << all_paths_this_guess[i][j].word << " " << hint_to_string(all_paths_this_guess[i][j].hint) << " ";
  1662.                     of << all_paths_this_guess[i][j].word << " " << hint_to_string(all_paths_this_guess[i][j].hint) << " ";
  1663.                 }
  1664.                 cout << endl;
  1665.                 of << endl;
  1666.             }
  1667.         }
  1668.  
  1669.         of.flush();
  1670.  
  1671.         if (guesses_so_far.size() == 1) {
  1672.             // print_optimal_solution_tree()
  1673.         }
  1674.     }
  1675.     return min_depth;
  1676. }
  1677.  
  1678. void hard_mode_analysis() {
  1679.     // I just realized that the correct strategy to optimize hard mode in Wordle is blindingly simple.
  1680.     // Because the guess pool size is reduced so much with each move, that means the move tree is very small.
  1681.     // That means it's totally computationally possible to branch-and-bound to exhaustively try every possible move tree.
  1682.     // And if I can heuristically find the longest paths first, I can do the bounding part even faster.
  1683.     // at kind of longest path search is more or less exactly what Absurdle is already doing
  1684.     vector<guess> guesses_so_far;
  1685.     ofstream of("normal_mode_log_sample_with_paths_new.txt");
  1686.     // 5 is SCOWL
  1687.  
  1688.     vector<vector<guess>> all_paths;
  1689.     int path_guesses_count = INT_MAX;
  1690.     hard_mode_get_tree_height(of, guess_words, answer_words, guesses_so_far, 2, 6, all_paths, path_guesses_count, true);
  1691. }
  1692.  
  1693. bool is_duplicate_guess(map<string, bool>& guess_classes, char* letters_in_answers, char* guess_word, bool add_nonoverlap_to_map) {
  1694.     string guess_class_str(guess_word);
  1695.     bool no_overlap = true;
  1696.     for (int cidx = 0; cidx < word_length; cidx++) {
  1697.         if (!letters_in_answers[guess_class_str[cidx]]) {
  1698.             guess_class_str[cidx] = '-';
  1699.             no_overlap = false;
  1700.         }
  1701.     }
  1702.     if (guess_classes.find(guess_class_str) != guess_classes.end()) {
  1703.         return true; // Duplicate on all letters occuring in answers, can skip
  1704.     }
  1705.     if (add_nonoverlap_to_map || !no_overlap) {
  1706.         guess_classes[guess_class_str] = true;
  1707.     }
  1708.     return false;
  1709. }
  1710.  
  1711. int total_print_guesses_count = 0;
  1712. int total_print_guesses_guesses = 0;
  1713. vector<int> print_guesses_stats;
  1714. map<string, bool> used_guess_prefixes;
  1715. map<char*, bool> words_seen;
  1716. ofstream print_guess_file("print_guesses.txt");
  1717. void reset_print_guesses() {
  1718.     total_print_guesses_count = 0;
  1719.     total_print_guesses_guesses = 0;
  1720.     used_guess_prefixes.clear();
  1721.     words_seen.clear();
  1722.     print_guesses_stats.clear();
  1723. }
  1724.  
  1725. void print_guesses(vector<guess> guesses) {
  1726.     if (print_guesses_stats.empty()) {
  1727.         print_guesses_stats.resize(7);
  1728.     }
  1729.     print_guesses_stats[guesses.size()]++;
  1730.  
  1731.     // Validate while we're at it
  1732.     release_assert(guesses.size() <= 5);
  1733.     release_assert(guesses[guesses.size() - 1].hint == num_hint_values - 1); // Last hint is all green
  1734.     words_seen[guesses[guesses.size() - 1].word] = true;
  1735.  
  1736.     // Prefixes must be unique otherwise the next move to take in a situation is ill-defined
  1737.     string guess_prefix;
  1738.     for (int i = 0; i < guesses.size() - 1; i++) {
  1739.         guess_prefix += guesses[i].word + hint_to_string(guesses[i].hint);
  1740.     }
  1741.     release_assert(used_guess_prefixes.find(guess_prefix) == used_guess_prefixes.end());
  1742.     used_guess_prefixes[guess_prefix] = true;
  1743.  
  1744.     // If this is zero it means the hints are inconsistent with the final word
  1745.     release_assert(get_valid_words(answer_words, guesses).size() == 1);
  1746.  
  1747.     // Count them up, make sure we get them all
  1748.     total_print_guesses_count++;
  1749.     total_print_guesses_guesses += guesses.size();
  1750.  
  1751.     for (int i = 0; i < guesses.size(); i++) {
  1752.         cout << guesses[i].word << " " << hint_to_string(guesses[i].hint) << " ";
  1753.         print_guess_file << guesses[i].word << " " << hint_to_string(guesses[i].hint) << " ";
  1754.     }
  1755.     cout << endl;
  1756.     print_guess_file << endl;
  1757.     print_guess_file.flush();
  1758. }
  1759.  
  1760. int can_solve_in(vector<char*>& valid_answers, int remaining_guesses, char* last_guess, hint h, vector<guess>& guesses_so_far, bool should_print, int max_total_guesses);
  1761.  
  1762. int can_solve_with_next_guess(char* current_guess, vector<char*>& valid_answers, int remaining_guesses, char* last_guess, hint h, vector<guess>& guesses_so_far, bool should_print, int max_total_guesses) {
  1763.     steady_clock::time_point start, stop;
  1764.     double time_sec;
  1765.     // if (remaining_guesses == 4) {
  1766.     //     start = high_resolution_clock::now();
  1767.     // }
  1768.     // if (remaining_guesses >= 4) cout << i << " " << current_guess << endl;
  1769.  
  1770.     vector<int> hint_class_counts;
  1771.     hint_class_counts.resize(num_hint_values);
  1772.  
  1773.     int max_count = 0;
  1774.     for (int m = 0; m < valid_answers.size(); m++) {
  1775.         hint hint = get_wordle_hints(current_guess, valid_answers[m]);
  1776.         if (++hint_class_counts[hint] > max_count) {
  1777.             max_count = hint_class_counts[hint];
  1778.         }
  1779.     }
  1780.  
  1781.     int total_guess_count = 0;
  1782.     bool passed_all_hint_classes = true;
  1783.     if (max_count == valid_answers.size()) {
  1784.         return INT_MAX; // This guess word gives no information, force skip it
  1785.     // } else if (max_count <= 2) {
  1786.     //     passed_all_hint_classes = (max_count <= remaining_guesses - 1);
  1787.     } else {
  1788.         // This lower bound assumes each hint class of size >= 3 will have a perfect split and resolve in two guesses
  1789.         int total_guess_count_lower_bound = 0;
  1790.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  1791.             // None of these will be the bottleneck otherwise max_count <= 2 above would have caught it
  1792.             if (hint_class_counts[h] == 0) {
  1793.                 ; // Do nothing
  1794.             } else if (h == num_hint_values - 1 /* all green */) {
  1795.                 total_guess_count_lower_bound += guesses_so_far.size() + 1;
  1796.             } else if (hint_class_counts[h] == 1) {
  1797.                 total_guess_count_lower_bound += guesses_so_far.size() + 2;
  1798.             } else if (hint_class_counts[h] == 2) {
  1799.                 total_guess_count_lower_bound += (guesses_so_far.size() + 2) + (guesses_so_far.size() + 3);
  1800.             } else {
  1801.                 total_guess_count_lower_bound += hint_class_counts[h] * ((guesses_so_far.size() + 1) + 2) - 1;
  1802.             }
  1803.             if (total_guess_count_lower_bound >= max_total_guesses) {
  1804.                 return INT_MAX;
  1805.             }
  1806.         }
  1807.  
  1808.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  1809.             // None of these will be the bottleneck otherwise max_count <= 2 above would have caught it
  1810.             if (hint_class_counts[h] == 0) {
  1811.                 ; // Do nothing
  1812.             } else if (!should_print && h == num_hint_values - 1 /* all green */) {
  1813.                 total_guess_count += guesses_so_far.size() + 1;
  1814.             } else if (!should_print && hint_class_counts[h] == 1) {
  1815.                 total_guess_count += guesses_so_far.size() + 2;
  1816.             } else if (!should_print && hint_class_counts[h] == 2) {
  1817.                 total_guess_count += (guesses_so_far.size() + 2) + (guesses_so_far.size() + 3);
  1818.             } else {
  1819.  
  1820.                 vector<char*> valid_answers_this_class = get_valid_words(valid_answers, current_guess, h);
  1821.  
  1822.                 guesses_so_far.push_back(guess(current_guess, h));
  1823.                 if (remaining_guesses == 5) cout << hint_to_string(h) << " " << valid_answers_this_class.size() << endl;
  1824.  
  1825.                 int result = can_solve_in(valid_answers_this_class, remaining_guesses - 1, current_guess, h, guesses_so_far, should_print /* should_print */, max_total_guesses - total_guess_count);
  1826.                 guesses_so_far.pop_back();
  1827.                 if (result == INT_MAX) {
  1828.                     // if (remaining_guesses == 4) {
  1829.                     //     stop = high_resolution_clock::now();
  1830.                     //     time_sec = duration_cast<microseconds>(stop - start).count() / 1000000.0;
  1831.                     // }
  1832.                     // if (!should_print && remaining_guesses == 4 && time_sec > 1.0) cout << last_guess << " " << hint_to_string(h) << " " << valid_answers.size() << " " << current_guess << " (XXX)" << endl;
  1833.                     return INT_MAX;
  1834.                 }
  1835.                 total_guess_count += result;
  1836.             }
  1837.             if (total_guess_count >= max_total_guesses) {
  1838.                 return INT_MAX;
  1839.             }
  1840.         }
  1841.     }
  1842.  
  1843.     // if (remaining_guesses == 4) {
  1844.     //     stop = high_resolution_clock::now();
  1845.     //     time_sec = duration_cast<microseconds>(stop - start).count() / 1000000.0;
  1846.     // }
  1847.  
  1848.     // if (!should_print && remaining_guesses == 4 && time_sec > 1.0) cout << last_guess << " " << hint_to_string(h) << " " << valid_answers.size() << " " << current_guess << endl;
  1849.  
  1850.     return total_guess_count;
  1851. }
  1852.  
  1853. int can_solve_in_forced(vector<char*>& valid_answers, int remaining_guesses, char* force_next_guess, char* last_guess, hint h, vector<guess>& guesses_so_far, bool should_print, int max_total_guesses) {
  1854. #if 0
  1855.     if (remaining_guesses == 5) {
  1856.         vector<int> hint_class_counts;
  1857.         hint_class_counts.resize(num_hint_values);
  1858.         char* current_guess = force_next_guess;
  1859.         int total_guess_count = 0;
  1860.  
  1861.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  1862.             vector<char*> valid_answers_this_class = get_valid_words(valid_answers, current_guess, h);
  1863.             if (valid_answers_this_class.size() > 0) {
  1864.                 guesses_so_far.push_back(guess(current_guess, h));
  1865.                 int result = can_solve_in_forced(valid_answers_this_class, remaining_guesses - 1, tokenized_words["HOTEL"], current_guess, h, guesses_so_far, true /* should_print */);
  1866.                 guesses_so_far.pop_back();
  1867.  
  1868.                 if (result == INT_MAX) {
  1869.                     return INT_MAX;
  1870.                 }
  1871.                 total_guess_count += result;
  1872.             }
  1873.         }
  1874.         return total_guess_count;
  1875.     }
  1876. #endif
  1877.  
  1878.     assert(valid_answers.size() > 0);
  1879.  
  1880.     if (remaining_guesses == 0) {
  1881.         return INT_MAX;
  1882.     }
  1883.  
  1884.     char* current_guess = force_next_guess;
  1885.     if (valid_answers.size() == 1) { // Special case, the forced guess gives no information so we have to avoid skipping it
  1886.         if (print_guesses) {
  1887.             guesses_so_far.push_back(guess(current_guess, valid_answers[0]));
  1888.             if (current_guess != valid_answers[0]) guesses_so_far.push_back(guess(valid_answers[0], valid_answers[0]));
  1889.             print_guesses(guesses_so_far);
  1890.             if (current_guess != valid_answers[0]) guesses_so_far.pop_back();
  1891.             guesses_so_far.pop_back();
  1892.         }
  1893.         return guesses_so_far.size() + (current_guess != valid_answers[0] ? 2 : 1);
  1894.     } else {
  1895.         return can_solve_with_next_guess(force_next_guess, valid_answers, remaining_guesses, last_guess, h, guesses_so_far, should_print, INT_MAX);
  1896.     }
  1897. }
  1898.  
  1899. vector<char*> rearrange_guesses(vector<char*>& valid_answers) {
  1900.     // Use letters in answers to optimize: all letters not occurring in answer words are equivalent and
  1901.     // will always give black/grey hints.
  1902.     char letters_in_answers[(int)'Z' + 1] = { 0 };
  1903.     for (int i = 0; i < valid_answers.size(); i++) {
  1904.         for (int cidx = 0; cidx < word_length; cidx++) {
  1905.             letters_in_answers[valid_answers[i][cidx]] = 1;
  1906.         }
  1907.     }
  1908.  
  1909.     // First filter out all guesses that are redundant because they are the same as another
  1910.     // guess except for the letters that are not present in the answer words.
  1911.     vector<char*> rearranged_guesses_1;
  1912.     {
  1913.         map<string, bool> guess_classes;
  1914.         for (int i = 0; i < valid_answers.size(); i++) {
  1915.             if (!is_duplicate_guess(guess_classes, letters_in_answers, valid_answers[i], true)) {
  1916.                 rearranged_guesses_1.push_back(valid_answers[i]);
  1917.             }
  1918.         }
  1919.         for (int i = 0; i < guess_words.size(); i++) {
  1920.             if (!is_duplicate_guess(guess_classes, letters_in_answers, guess_words[i], false)) {
  1921.                 rearranged_guesses_1.push_back(guess_words[i]);
  1922.             }
  1923.         }
  1924.     }
  1925.  
  1926.     // Now sort the guesses by max hint class size.
  1927.     vector<char*> rearranged_guesses_2;
  1928.     rearranged_guesses_2.resize(rearranged_guesses_1.size());
  1929.     {
  1930.         vector<int> max_hint_class_size;
  1931.         max_hint_class_size.resize(rearranged_guesses_1.size());
  1932.         for (int i = 0; i < rearranged_guesses_1.size(); i++) {
  1933.             memset(hint_class_counts, 0, sizeof(int) * num_hint_values);
  1934.             int max_count = 0;
  1935.             for (int m = 0; m < valid_answers.size(); m++) {
  1936.                 hint hint = get_wordle_hints(rearranged_guesses_1[i], valid_answers[m]);
  1937.                 if (++hint_class_counts[hint] > max_count) {
  1938.                     max_count = hint_class_counts[hint];
  1939.                 }
  1940.             }
  1941.             max_hint_class_size[i] = max_count;
  1942.         }
  1943.  
  1944.         vector<size_t> idx(max_hint_class_size.size());
  1945.         iota(idx.begin(), idx.end(), 0);
  1946.         sort(idx.begin(), idx.end(), [&max_hint_class_size](size_t i, size_t j) {
  1947.             return max_hint_class_size[i] < max_hint_class_size[j];
  1948.         });
  1949.  
  1950.         for (int i = 0; i < rearranged_guesses_2.size(); i++) {
  1951.             rearranged_guesses_2[i] = rearranged_guesses_1[idx[i]];
  1952.         }
  1953.     }
  1954.  
  1955.     return rearranged_guesses_2;
  1956. }
  1957.  
  1958. int can_solve_in(vector<char*>& valid_answers, int remaining_guesses, char* last_guess, hint h, vector<guess>& guesses_so_far, bool should_print, int max_total_guesses) {
  1959.     assert(valid_answers.size() > 0);
  1960.  
  1961.     if (remaining_guesses == 0) {
  1962.         return INT_MAX;
  1963.     }
  1964.  
  1965.     if (remaining_guesses == 1 || valid_answers.size() == 1) {
  1966.         if (should_print && valid_answers.size() == 1) {
  1967.             if (last_guess == valid_answers[0]) {
  1968.                 print_guesses(guesses_so_far);
  1969.             } else {
  1970.                 guesses_so_far.push_back(guess(valid_answers[0], num_hint_values - 1 /* all green */));
  1971.                 print_guesses(guesses_so_far);
  1972.                 guesses_so_far.pop_back();
  1973.             }
  1974.         }
  1975.         if (valid_answers.size() == 1) {
  1976.             return guesses_so_far.size() + (last_guess == valid_answers[0] ? 0 : 1);
  1977.         } else {
  1978.             return INT_MAX;
  1979.         }
  1980.     }
  1981.  
  1982.     if (valid_answers.size() <= 2 /*remaining_guesses*/) { // Can just guess them all one by one
  1983.         if (should_print) for (int i = 0; i < valid_answers.size(); i++) {
  1984.             for (int j = 0; j <= i; j++) {
  1985.                 guesses_so_far.push_back(guess(valid_answers[j], valid_answers[i]));
  1986.             }
  1987.             print_guesses(guesses_so_far);
  1988.             for (int j = 0; j <= i; j++) {
  1989.                 guesses_so_far.pop_back();
  1990.             }
  1991.         }
  1992.        
  1993.         // sum i=0^(valid_answers.size()-1) (guesses_so_far.size() + i + 1)
  1994.         return guesses_so_far.size()*valid_answers.size() + valid_answers.size()*(valid_answers.size() + 1)/2;
  1995.     }
  1996.  
  1997.     if (valid_answers.size() * (guesses_so_far.size() + 2) - 1 >= max_total_guesses) {
  1998.         return INT_MAX;
  1999.     }
  2000.  
  2001.     bool is_valid_word;
  2002.     char* perfect_split_guess = get_perfect_split(valid_answers, is_valid_word);
  2003.  
  2004.     if (remaining_guesses == 2 || perfect_split_guess != NULL) {
  2005.         if (perfect_split_guess == NULL) {
  2006.             return INT_MAX;
  2007.         }
  2008.  
  2009.         if (should_print) for (int i = 0; i < valid_answers.size(); i++) {
  2010.             guesses_so_far.push_back(guess(perfect_split_guess, valid_answers[i]));
  2011.             if (perfect_split_guess != valid_answers[i]) {
  2012.                 guesses_so_far.push_back(guess(valid_answers[i], num_hint_values - 1 /* all green */));
  2013.             }
  2014.             print_guesses(guesses_so_far);
  2015.             if (perfect_split_guess != valid_answers[i]) {
  2016.                 guesses_so_far.pop_back();
  2017.             }
  2018.             guesses_so_far.pop_back();
  2019.         }
  2020.  
  2021.         return valid_answers.size() * (guesses_so_far.size() + 2)  -  (is_valid_word ? 1 : 0);
  2022.     }
  2023.  
  2024.     // Now things get more interesting. We must have at least 3 guesses left and must need at least 3.
  2025.     assert(remaining_guesses >= 3);
  2026.  
  2027.     vector<char*> rearranged_guesses = remaining_guesses == 5 ? guess_words : rearrange_guesses(valid_answers);
  2028.  
  2029.     // Compute upper bound on total guesses for each next guess, use that to lower max_total_guesses
  2030.     for (int i = 0; i < rearranged_guesses.size(); i++) {
  2031.         vector<int> hint_class_counts;
  2032.         hint_class_counts.resize(num_hint_values);
  2033.  
  2034.         int max_count = 0;
  2035.         for (int m = 0; m < valid_answers.size(); m++) {
  2036.             hint hint = get_wordle_hints(rearranged_guesses[i], valid_answers[m]);
  2037.             if (++hint_class_counts[hint] > max_count) {
  2038.                 max_count = hint_class_counts[hint];
  2039.             }
  2040.         }
  2041.  
  2042.         // This upper bound assumes each hint class of size >= 3 will have a perfect split and resolve in two guesses
  2043.         int total_guess_count_upper_bound = 0;
  2044.         for (hint h = num_hint_values - 1; h >= 0 && h < num_hint_values; h--) {
  2045.             // None of these will be the bottleneck otherwise max_count <= 2 above would have caught it
  2046.             if (hint_class_counts[h] == 0) {
  2047.                 ; // Do nothing
  2048.             } else if (h == num_hint_values - 1 /* all green */) {
  2049.                 total_guess_count_upper_bound += guesses_so_far.size() + 1;
  2050.             } else if (hint_class_counts[h] == 1) {
  2051.                 total_guess_count_upper_bound += guesses_so_far.size() + 2;
  2052.             } else if (hint_class_counts[h] == 2) {
  2053.                 total_guess_count_upper_bound += (guesses_so_far.size() + 2) + (guesses_so_far.size() + 3);
  2054.             } else {
  2055.                 // guess each word one by one: sum i=0^(valid_answers.size()-1) ((guesses_so_far.size()+1) + i + 1)
  2056.                 total_guess_count_upper_bound += (guesses_so_far.size() + 1)*hint_class_counts[h] + hint_class_counts[h]*(hint_class_counts[h] + 1)/2;
  2057.             }
  2058.             if (total_guess_count_upper_bound >= max_total_guesses) {
  2059.                 break;
  2060.             }
  2061.         }
  2062.         total_guess_count_upper_bound++; // Increase by 1 because we skip on equality
  2063.         if (total_guess_count_upper_bound < max_total_guesses) {
  2064.             max_total_guesses = total_guess_count_upper_bound;
  2065.         }
  2066.     }
  2067.  
  2068.     // if (remaining_guesses == 4) {
  2069.     //     cout << "Upper bound: " << max_total_guesses << endl;
  2070.     // }
  2071.  
  2072.     int best_result = max_total_guesses;
  2073.     char* best_guess = NULL;
  2074.     char* crate = tokenized_words["CRATE"];
  2075.     char* opsin = tokenized_words["OPSIN"];
  2076.     for (int i = 0; i < rearranged_guesses.size(); i++) {
  2077.         if (remaining_guesses == 4 && guesses_so_far[0].word == crate && h == string_to_hint("-y--y")) {
  2078.             if (rearranged_guesses[i] != opsin) {
  2079.                 continue;
  2080.             }
  2081.         }
  2082.         if (remaining_guesses == 5) {
  2083.             cout << i << " " << rearranged_guesses[i] << endl;
  2084.             print_guess_file << i << " " << rearranged_guesses[i] << endl;
  2085.             reset_print_guesses();
  2086.         }
  2087.         int result = can_solve_with_next_guess(rearranged_guesses[i], valid_answers, remaining_guesses, last_guess, h, guesses_so_far, remaining_guesses == 5 ? true : false /* should_print */, best_result);
  2088.         if (result < best_result) {
  2089.             best_result = result;
  2090.             best_guess = rearranged_guesses[i];
  2091.             if (remaining_guesses == 5) {
  2092.                 release_assert(total_print_guesses_count == answer_words.size());
  2093.                 release_assert(best_result == total_print_guesses_guesses);
  2094.                 cout << "Guess stats:" << endl;
  2095.                 print_guess_file << "Guess stats:" << endl;
  2096.                 for (int num_guesses = 1; num_guesses <= 6; num_guesses++) {
  2097.                     cout << num_guesses << ": " << print_guesses_stats[num_guesses] << endl;
  2098.                     print_guess_file << num_guesses << ": " << print_guesses_stats[num_guesses] << endl;
  2099.                 }
  2100.                 cout << "Total guesses: " << best_result << endl;
  2101.                 print_guess_file << "Total guesses: " << best_result << endl;
  2102.             }
  2103.             // if (remaining_guesses == 4) {
  2104.             //     cout << "Best so far: " << best_guess << " " << best_result << endl;
  2105.             // }
  2106.         }
  2107.     }
  2108.  
  2109.     if (should_print && best_guess != NULL) {
  2110.         can_solve_with_next_guess(best_guess, valid_answers, remaining_guesses, last_guess, h, guesses_so_far, true /*should_print*/, best_result + 1);
  2111.     }
  2112.  
  2113.     return best_result;
  2114. }
  2115.  
  2116. void optimal_scan_normal() {
  2117.     vector<guess> guesses_so_far;
  2118.  
  2119.     int total_guesses = can_solve_in(answer_words, 5, NULL, 0, guesses_so_far, true /* should_print */, INT_MAX);
  2120.     for (int i = 0; i < answer_words.size(); i++) {
  2121.         if (words_seen.find(answer_words[i]) == words_seen.end()) {
  2122.             cout << "Missing: " << answer_words[i] << endl;
  2123.             release_assert(false);
  2124.         }
  2125.     }
  2126.     release_assert(total_print_guesses_count == answer_words.size());
  2127.     release_assert(total_guesses == total_print_guesses_guesses);
  2128.     cout << "Total guesses: " << total_guesses;
  2129.     return;
  2130.  
  2131.     ofstream of("depth_5.txt");
  2132.     auto start = high_resolution_clock::now();
  2133.     for (int i = 0; i < guess_words.size(); i++) {
  2134.         if (can_solve_in(answer_words, 5, guess_words[i], 0, guesses_so_far, true /* should_print */, INT_MAX)) {
  2135.             cout << guess_words[i] << " : solution of depth 5 exists" << endl;
  2136.             of << guess_words[i] << " : solution of depth 5 exists" << endl;
  2137.         } else {
  2138.             cout << guess_words[i] << " : NO SOLUTION OF DEPTH 5 EXISTS" << endl;
  2139.             of << guess_words[i] << " : NO SOLUTION OF DEPTH 5 EXISTS" << endl;
  2140.         }
  2141.     }
  2142.     auto stop = high_resolution_clock::now();
  2143.     cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  2144. }
  2145.  
  2146. int main(int argc, char* argv[]) {
  2147. #if 0
  2148.     for (word_length = 1; word_length <= 500; word_length++) {
  2149.         num_hint_values = 1;
  2150.         for (int i = 0; i < word_length; i++) {
  2151.             num_hint_values *= 3;
  2152.         }
  2153.  
  2154.         guess_words = read_word_file(GUESS_WORD_FILE);
  2155.         if (guess_words.size() > 0) {
  2156.             cout << "Valid guesses : " << guess_words.size() << " words of length " << word_length << " in " << GUESS_WORD_FILE << endl;
  2157.         }
  2158.         if (strcmp(GUESS_WORD_FILE, ANSWER_WORD_FILE) == 0) {
  2159.             answer_words = guess_words;
  2160.         } else {
  2161.             answer_words = read_word_file(ANSWER_WORD_FILE);
  2162.         }
  2163.         if (answer_words.size() > 0) {
  2164.             cout << "Valid answers : " << answer_words.size() << " words of length " << word_length << " in " << ANSWER_WORD_FILE << endl;
  2165.         }
  2166.         if (answer_words.size() == 0 || guess_words.size() == 0) {
  2167.             continue;
  2168.         }
  2169.  
  2170.         if (should_prefer_dense(answer_words.size())) {
  2171.             hint_class_counts = new int[num_hint_values];
  2172.         }
  2173.  
  2174.         best_first_guess = NULL;
  2175.         find_best_first_guess();
  2176.  
  2177.         delete[] hint_class_counts;
  2178.         hint_class_counts = NULL;
  2179.     }
  2180.     return 0;
  2181. #endif
  2182.  
  2183.     string newline;
  2184.     cout << "Enter word length: ";
  2185.     cin >> word_length;
  2186.     getline(cin, newline);
  2187.  
  2188.     num_hint_values = 1;
  2189.     for (int i = 0; i < word_length; i++) {
  2190.         num_hint_values *= 3;
  2191.     }
  2192.  
  2193.     guess_words = read_word_file(GUESS_WORD_FILE);
  2194.     cout << "Valid guesses : " << guess_words.size() << " words of length " << word_length << " in " << GUESS_WORD_FILE << endl;
  2195.     if (strcmp(GUESS_WORD_FILE, ANSWER_WORD_FILE) == 0) {
  2196.         answer_words = guess_words;
  2197.     } else {
  2198.         answer_words = read_word_file(ANSWER_WORD_FILE);
  2199.     }
  2200.     cout << "Valid answers : " << answer_words.size() << " words of length " << word_length << " in " << ANSWER_WORD_FILE << endl;
  2201.  
  2202.     if (answer_words.size() == 0 || guess_words.size() == 0) {
  2203.         cout << "No words of length " << word_length << " in wordlists, exiting." << endl;
  2204.         return 1;
  2205.     }
  2206.  
  2207.     if (should_prefer_dense(answer_words.size())) {
  2208.         hint_class_counts = new int[num_hint_values];
  2209.     }
  2210.  
  2211.     // Precompute letter masks for guess words
  2212.     guess_words_letter_masks.resize(guess_words.size());
  2213.     for (int i = 0; i < guess_words.size(); i++) {
  2214.         int letter_mask = 0;
  2215.         for (int cidx = 0; cidx < word_length; cidx++) {
  2216.             letter_mask |= 1 << (guess_words[i][cidx] - 'A');
  2217.         }
  2218.         guess_words_letter_masks[i] = letter_mask;
  2219.     }
  2220.  
  2221. #if 0
  2222.     map<int, bool> letter_masks;
  2223.     for (int i = 0; i < guess_words.size(); i++) {
  2224.         int letter_mask = 0;
  2225.         for (int cidx = 0; cidx < word_length; cidx++) {
  2226.             letter_mask |= 1 << (guess_words[i][cidx] - 'A');
  2227.         }
  2228.         letter_masks[letter_mask] = true;
  2229.     }
  2230.     cout << letter_masks.size() << endl; // 7632
  2231.  
  2232.     map<int, bool> letter_masks_two;
  2233.     for (int i = 0; i < guess_words.size(); i++) {
  2234.         for (int j = 0; j < guess_words.size(); j++) {
  2235.             if (i == j) continue;
  2236.             int letter_mask = 0;
  2237.             for (int cidx = 0; cidx < word_length; cidx++) {
  2238.                 letter_mask |= 1 << (guess_words[i][cidx] - 'A');
  2239.                 letter_mask |= 1 << (guess_words[j][cidx] - 'A');
  2240.             }
  2241.             letter_masks_two[letter_mask] = true;
  2242.         }
  2243.     }
  2244.     cout << letter_masks_two.size() << endl; // 2086895
  2245.  
  2246.     vector<int> letters_two;
  2247.     for (auto iter = letter_masks_two.begin(); iter != letter_masks_two.end(); iter++) {
  2248.         letters_two.push_back(iter->first);
  2249.     }
  2250.  
  2251.     int* answer_letter_masks = new int[answer_words.size()];
  2252.     for (int i = 0; i < answer_words.size(); i++) {
  2253.         int letter_mask = 0;
  2254.         for (int cidx = 0; cidx < word_length; cidx++) {
  2255.             letter_mask |= 1 << (answer_words[i][cidx] - 'A');
  2256.         }
  2257.         answer_letter_masks[i] = letter_mask;
  2258.     }
  2259.  
  2260.     auto start = high_resolution_clock::now();
  2261.  
  2262.     for (int i = 0; i < letters_two.size(); i++) {
  2263.         int count = 0;
  2264.         for (int j = 0; j < answer_words.size(); j++) {
  2265.             if ((answer_letter_masks[j] & letters_two[i]) == 0) {
  2266.                 count++;
  2267.                 if (count >= 2) break;
  2268.             }
  2269.         }
  2270.         if (count <= 1) {
  2271.             cout << "3 guess solution MAY exist" << endl;
  2272.         }
  2273.     }
  2274.  
  2275.     auto stop = high_resolution_clock::now();
  2276.     cout << "Done, time: " << duration_cast<microseconds>(stop - start).count() / 1000000.0 << " s" << endl;
  2277.  
  2278. #endif
  2279.  
  2280. #if 0
  2281.     map<string, vector<char*>> answer_words_minus_one_letter;
  2282.     for (int i = 0; i < answer_words.size(); i++) {
  2283.         for (int cidx = 0; cidx < word_length; cidx++) {
  2284.             string word_str = answer_words[i];
  2285.             word_str[cidx] = '-';
  2286.             answer_words_minus_one_letter[word_str].push_back(answer_words[i]);
  2287.         }
  2288.     }
  2289.  
  2290.  
  2291.     map<string, vector<char*>> answer_words_minus_one_letter;
  2292.     for (int i = 0; i < answer_words.size(); i++) {
  2293.         for (int cidx = 0; cidx < word_length; cidx++) {
  2294.             string word_str = answer_words[i];
  2295.             word_str[cidx] = '-';
  2296.             answer_words_minus_one_letter[word_str].push_back(answer_words[i]);
  2297.         }
  2298.     }
  2299.     map<string, bool> constraint_map;
  2300.     for (int i = 0; i < answer_words.size(); i++) {
  2301.         for (int cidx = 0; cidx < word_length; cidx++) {
  2302.             string word_str = answer_words[i];
  2303.             word_str[cidx] = '-';
  2304.             vector<char*> word_list = answer_words_minus_one_letter[word_str];
  2305.             if (word_list.size() > 1 && word_list[0] == answer_words[i]) {
  2306.                 string constraint;
  2307.                 for (int j = 0; j < word_list.size(); j++) {
  2308.                     constraint += word_list[j][cidx];
  2309.                 }
  2310.                 constraint_map[constraint] = true;
  2311.             }
  2312.         }
  2313.     }
  2314.     vector<string> constraints;
  2315.     for (auto iter = constraint_map.begin(); iter != constraint_map.end(); iter++) {
  2316.         constraints.push_back(iter->first);
  2317.     }
  2318.     for (int i = 0; i < constraints.size(); i++) {
  2319.         cout << constraints[i] << endl;
  2320.     }
  2321.  
  2322.     map<string, vector<char*>> answer_words_minus_two_letters;
  2323.     for (int i = 0; i < answer_words.size(); i++) {
  2324.         for (int c = 0; c < word_length; c++) {
  2325.             for (int d = 0; d < word_length; d++) {
  2326.                 string word_str = answer_words[i];
  2327.             word_str[cidx] = '-';
  2328.             answer_words_minus_one_letter[word_str].push_back(answer_words[i]);
  2329.         }
  2330.     }
  2331.     map<string, bool> constraint_map;
  2332.     for (int i = 0; i < answer_words.size(); i++) {
  2333.         for (int cidx = 0; cidx < word_length; cidx++) {
  2334.             string word_str = answer_words[i];
  2335.             word_str[cidx] = '-';
  2336.             vector<char*> word_list = answer_words_minus_one_letter[word_str];
  2337.             if (word_list.size() > 1 && word_list[0] == answer_words[i]) {
  2338.                 string constraint;
  2339.                 for (int j = 0; j < word_list.size(); j++) {
  2340.                     constraint += word_list[j][cidx];
  2341.                 }
  2342.                 constraint_map[constraint] = true;
  2343.             }
  2344.         }
  2345.     }
  2346.     vector<string> constraints;
  2347.     for (auto iter = constraint_map.begin(); iter != constraint_map.end(); iter++) {
  2348.         constraints.push_back(iter->first);
  2349.     }
  2350.     for (int i = 0; i < constraints.size(); i++) {
  2351.         cout << constraints[i] << endl;
  2352.     }
  2353.  
  2354.     int min_cover = -1;
  2355.     int min_cover_size = INT_MAX;
  2356.     for (int i = 0; i < 67108864 /*2^26*/; i++) {
  2357.         bool failed = false;
  2358.         for (int j = 0; j < constraints.size(); j++) {
  2359.             int satisfied_count = 0;
  2360.             for (int cidx = 0; cidx < constraints[j].length(); cidx++) {
  2361.                 if (i & (1 << constraints[j][cidx] - 'A')) {
  2362.                     satisfied_count++;
  2363.                 }
  2364.             }
  2365.             if (satisfied_count < constraints[j].length() - 1) { // All but 1 must be satisfied
  2366.                 failed = true;
  2367.                 break;
  2368.             }
  2369.         }
  2370.         if (failed) {
  2371.             continue;
  2372.         }
  2373.  
  2374.         int cover_size = 0;
  2375.         for (int letter_idx = 0; letter_idx < 26; letter_idx++) {
  2376.             if (i & (1 << letter_idx)) {
  2377.                 cover_size++;
  2378.             }
  2379.         }
  2380.         if (cover_size < min_cover_size) {
  2381.             min_cover = i;
  2382.             min_cover_size = cover_size;
  2383.             cout << "Best cover found so far: ";
  2384.             for (int letter_idx = 0; letter_idx < 26; letter_idx++) {
  2385.                 if (i & (1 << letter_idx)) {
  2386.                     cout << ((char)(letter_idx + 'A'));
  2387.                 }
  2388.             }
  2389.             cout << " " << min_cover_size << endl;
  2390.         } else if (cover_size == min_cover_size) {
  2391.             min_cover = i;
  2392.             min_cover_size = cover_size;
  2393.             cout << "Same quality: ";
  2394.             for (int letter_idx = 0; letter_idx < 26; letter_idx++) {
  2395.                 if (i & (1 << letter_idx)) {
  2396.                     cout << ((char)(letter_idx + 'A'));
  2397.                 }
  2398.             }
  2399.             cout << " " << min_cover_size << endl;
  2400.         }
  2401.     }
  2402. #endif
  2403.  
  2404.  
  2405.     while (true) {
  2406.         cout << "Select one:" << endl;
  2407.         cout << " 1. Find best first guess" << endl;
  2408.         cout << " 2. Guess unknown word (solve puzzle)" << endl;
  2409.         cout << " 3. Show solution for a single given word" << endl;
  2410.         cout << " 4. Solve all words in dictionary and show statistics" << endl;
  2411.         cout << " 5. Find best set of 3 words to start out with" << endl;
  2412.         cout << " 6. Show largest hint class of a set of words" << endl;
  2413.         cout << " 7. Get best 4th and 5th words" << endl;
  2414.         cout << " 8. Hard mode analysis" << endl;
  2415.         cout << " 9. Optimal normal mode analysis" << endl;
  2416.         cout << "10. Quit" << endl;
  2417.         cout << "Enter your choice: ";
  2418.         int choice;
  2419.         cin >> choice;
  2420.         getline(cin, newline);
  2421.  
  2422.         switch (choice) {
  2423.         case 1:
  2424.             find_best_first_guess();
  2425.             break;
  2426.         case 2:
  2427.             guess_unknown_word();
  2428.             break;
  2429.         case 3:
  2430.             solve_single_word();
  2431.             break;
  2432.         case 4:
  2433.             solve_all_words_in_dictionary();
  2434.             break;
  2435.         case 5:
  2436.             if (argc >= 3) {
  2437.                 thread_num = atoi(argv[1]);
  2438.                 num_threads = atoi(argv[2]);
  2439.             } else {
  2440.                 thread_num = 0;
  2441.                 num_threads = 1;
  2442.             }
  2443.             best_first_k();
  2444.             break;
  2445.         case 6:
  2446.             show_largest_hint_class();
  2447.             break;
  2448.         case 7:
  2449.             best_4th();
  2450.             break;
  2451.         case 8:
  2452.             hard_mode_analysis();
  2453.             break;
  2454.         case 9:
  2455.             optimal_scan_normal();
  2456.             break;
  2457.         case 10:
  2458.             return 0;
  2459.         default:
  2460.             cout << "Invalid option selected." << endl;
  2461.             break;
  2462.         }
  2463.     }
  2464. }
  2465.  
Advertisement
Add Comment
Please, Sign In to add comment