diff --git a/ordle.cc b/ordle.cc index 5c24777..33b5bda 100644 --- a/ordle.cc +++ b/ordle.cc @@ -1,101 +1,69 @@ -#include -#include -#include +#include "ordle.h" #include -#include -#include +#include #include +#include +#include +#include -// Helper types and aliases -using size_type = std::string::size_type; -using letters_and_indices = std::map; - -// Function to read candidates from a file std::vector read_candidates(std::istream &input) { std::vector candidates; - std::string word; + std::string line; - while (input >> word) { - if (word.size() == 5) { - std::transform(word.begin(), word.end(), word.begin(), ::tolower); - candidates.push_back(word); + while (std::getline(input, line)) { + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + + if (line.size() == 5) { + std::transform(line.begin(), line.end(), line.begin(), ::tolower); + candidates.push_back(line); } } - // Remove duplicates std::sort(candidates.begin(), candidates.end()); - candidates.erase(std::unique(candidates.begin(), candidates.end()), candidates.end()); + + candidates.erase(std::unique(candidates.begin(), candidates.end()), + candidates.end()); return candidates; } -// Helper functions -bool contains_any_of(const std::string &s, const std::string &cs) { - return std::any_of(cs.begin(), cs.end(), [&s](char c) { return s.find(c) != std::string::npos; }); +bool wrong_fn::operator()(const std::string &word) const { + return contains_any_of(word, l); } -bool contains_at(const std::string &s, char c, size_type pos) { - return pos < s.size() && s[pos] == c; +bool correct_fn::operator()(const std::string &word) const { + return std::all_of(m.begin(), m.end(), [&word](const auto &pair) { + return contains_at(word, pair.second[0], pair.first); + }); } -bool contains_but_not_at(const std::string &s, char c, size_type pos) { - return s.find(c) != std::string::npos && !contains_at(s, c, pos); +bool misplaced_fn::operator()(const std::string &word) const { + return std::all_of(m.begin(), m.end(), [&word](const auto &pair) { + auto a = contains_but_not_at(word, pair.second[0], pair.first); + if (a) + std::cout << "Misplaced: " << word << std::endl; + return a; + }); } -// Functor for wrong letters -struct wrong_fn { - explicit wrong_fn(const std::string &letters) : l{letters} {} - - bool operator()(const std::string &word) const { - return contains_any_of(word, l); - } - -private: - std::string l; -}; - -// Functor for correct letters -struct correct_fn { - explicit correct_fn(const letters_and_indices &idxs) : m{idxs} {} - - bool operator()(const std::string &word) const { - return std::all_of(m.begin(), m.end(), [&word](const auto &pair) { - return contains_at(word, pair.second[0], pair.first); - }); - } - -private: - letters_and_indices m; -}; - -// Functor for misplaced letters -struct misplaced_fn { - explicit misplaced_fn(const letters_and_indices &idxs) : m{idxs} {} - - bool operator()(const std::string &word) const { - return std::all_of(m.begin(), m.end(), [&word](const auto &pair) { - return contains_but_not_at(word, pair.second[0], pair.first); - }); - } - -private: - letters_and_indices m; -}; - -// Function to filter candidates void do_filter(std::vector &candidates, const std::string &wrong, - const letters_and_indices &green, const letters_and_indices &yellow) { + const IndexMap &green, const IndexMap &yellow) { auto predicate = [&wrong, &green, &yellow](const std::string &word) { - return wrong_fn(wrong)(word) || !correct_fn(green)(word) || !misplaced_fn(yellow)(word); + return wrong_fn(wrong)(word) || !correct_fn(green)(word) || + !misplaced_fn(yellow)(word); }; - candidates.erase(std::remove_if(candidates.begin(), candidates.end(), predicate), candidates.end()); + // Remove all words that do not satisfy the conditions + candidates.erase( + std::remove_if(candidates.begin(), candidates.end(), predicate), + candidates.end()); } -// User interaction function -letters_and_indices build_list(const std::string &line) { +IndexMap build_list(const std::string &line) { std::istringstream iss(line); - letters_and_indices result; + IndexMap result; char letter; size_type index; @@ -106,7 +74,7 @@ letters_and_indices build_list(const std::string &line) { return result; } -std::tuple prompt() { +std::tuple prompt() { std::string wrong; std::cout << "Enter wrong letters:\n"; std::getline(std::cin, wrong); @@ -122,4 +90,4 @@ std::tuple prompt() { auto yellow = build_list(misplaced); return {wrong, green, yellow}; -} \ No newline at end of file +} diff --git a/ordle.h b/ordle.h index e328e96..ac428df 100644 --- a/ordle.h +++ b/ordle.h @@ -1,54 +1,213 @@ #ifndef ORDLE_H #define ORDLE_H -#include -#include -#include -#include #include -#include +#include +#include +#include #include +#include + +// TODO: Evaluate need for helpers // Helper types and aliases using size_type = std::string::size_type; -using letters_and_indices = std::map; -// Function declarations +/** + * @brief A mapping of indices to letters or strings. + * + * This type alias represents a map where each key is a position (index), and + * the value is a string (typically a single letter). It is used to track the + * correct or misplaced letter positions in word-guessing games, like Wordle, or + * similar puzzles. + * + * @note The `std::map` ensures that the indices are stored in sorted order, + * which is useful for efficiently checking conditions related to the positions + * of letters within words. + * + * @see correct_fn, misplaced_fn, build_list + */ +using IndexMap = std::map; + +/** + * Reads a list of words from an input stream where each word is separated by a + * newline. + * + * @param input The input stream containing newline-separated words. + * @return A vector containing all the words from the input stream. + */ std::vector read_candidates(std::istream &input); -bool contains_any_of(const std::string &s, const std::string &cs); -bool contains_at(const std::string &s, char c, size_type pos); -bool contains_but_not_at(const std::string &s, char c, size_type pos); +/** + * Checks if a string contains any character from a given set of characters. + * + * @param s The string to search within. + * @param cs A string containing the set of characters to look for. + * @return true if any character in 'cs' is found in 's'; otherwise, false. + */ +inline bool contains_any_of(const std::string &s, const std::string &cs) { + return std::any_of(cs.begin(), cs.end(), + [&s](char c) { return s.find(c) != std::string::npos; }); +} -// Functors +/** + * Checks if a string contains a specific character at a given position. + * + * @param s The string to search within. + * @param c The character to check for. + * @param pos The position to check within the string. + * @return true if the character 'c' is found at position 'pos' in the string + * 's'; otherwise, false. + */ +inline bool contains_at(const std::string &s, char c, size_type pos) { + return pos < s.size() && s[pos] == c; +} + +/** + * Checks if a string contains a specific character, but not at a given + * position. + * + * @param s The string to search within. + * @param c The character to check for. + * @param pos The position where the character must not be located. + * @return true if the character 'c' is found in the string 's' and not at + * position 'pos'; otherwise, false. + */ +inline bool contains_but_not_at(const std::string &s, char c, size_type pos) { + for (size_type i = 0; i < s.size(); ++i) { + if (s[i] == c && i != pos) { + return true; // Found letter but not at the correct position + } + } + return false; // Letter not found or in the correct index +} + +/** + * A functor to filter words containing any of the specified letters. + */ struct wrong_fn { - explicit wrong_fn(const std::string &letters); + /** + * Constructs a functor with the specified letters to filter against. + * + * @param letters A string containing the letters that words should not + * contain. + */ + wrong_fn(const std::string &letters) : l{letters} {} + + /** + * Checks if a word contains any of the specified "wrong" letters. + * + * @param word The word to be checked. + * @return True if the word contains any "wrong" letters; false otherwise. + */ bool operator()(const std::string &word) const; -private: - std::string l; + private: + std::string l; ///< The letters considered "wrong". }; +/** + * A functor to filter words based on exact character positions. + */ struct correct_fn { - explicit correct_fn(const letters_and_indices &idxs); + /** + * Constructs a functor with the specified indices and characters. + * + * @param idxs A map of indices to characters representing the exact + * matches. + */ + correct_fn(const IndexMap &idxs) : m{idxs} {} + + /** + * Checks if a word contains the correct characters at the specified + * indices. + * + * @param word The word to be checked. + * @return True if the word matches the correct indices; false otherwise. + */ bool operator()(const std::string &word) const; -private: - letters_and_indices m; + private: + IndexMap m; ///< Map of indices to expected characters. }; +/** + * A functor to filter words based on misplaced characters. + */ struct misplaced_fn { - explicit misplaced_fn(const letters_and_indices &idxs); + /** + * Constructs a functor with the specified indices and characters. + * + * @param idxs A map of indices to characters representing misplaced + * matches. + */ + misplaced_fn(const IndexMap &idxs) : m{idxs} {} + + /** + * Checks if a word contains the specified characters but at incorrect + * positions. + * + * @param word The word to be checked. + * @return True if the word contains the characters but at incorrect + * positions; false otherwise. + */ bool operator()(const std::string &word) const; -private: - letters_and_indices m; + private: + IndexMap m; ///< Map of indices to misplaced characters. }; +/** + * Filters the `candidates` list by removing words that do not meet certain + * conditions based on the given `wrong`, `green`, and `yellow` parameters. + * + * The conditions are as follows: + * - A word is removed if it contains any of the incorrect letters specified in + * `wrong`. + * - A word is removed if it does not match the positions of the letters + * specified in `green` (green letters must be in the exact same positions). + * - A word is removed if it does not correctly place the yellow letters + * specified in `yellow` (yellow letters must be in the word but at different + * positions). + * + * @param candidates A reference to the vector of candidate words to filter. + * @param wrong A string of incorrect letters that should not appear in the + * word. + * @param green An `IndexMap` representing the correct letters and their exact + * positions in the word. + * @param yellow An `IndexMap` representing the letters that should appear in + * the word but in incorrect positions. + */ void do_filter(std::vector &candidates, const std::string &wrong, - const letters_and_indices &green, const letters_and_indices &yellow); + const IndexMap &green, const IndexMap &yellow); -letters_and_indices build_list(const std::string &line); -std::tuple prompt(); +/** + * Constructs an `IndexMap` from the provided string. + * The function parses the string and maps the indices of letters to their + * respective positions in a way that is suitable for the filtering logic in the + * Wordle solver. + * + * @param line A string representing the word or feedback line to process (e.g., + * "G__Y_"). + * + * @return An `IndexMap` where the key is the index of the letter, and the value + * represents the status of that letter. This could be an enumeration or an + * integer representing the state of the letter (e.g., green, yellow, or gray). + */ +IndexMap build_list(const std::string &line); + +/** + * Prompts the user (or simulation) for input, typically to get a guess and + * feedback from the user. This function returns the guessed word along with its + * green and yellow feedback in the form of `IndexMap`s. + * + * @return A tuple containing three elements: + * - A string representing the guessed word. + * - An `IndexMap` for the positions and letters that are correct (green + * letters). + * - An `IndexMap` for the positions and letters that are in the word + * but misplaced (yellow letters). + */ +std::tuple prompt(); #endif // ORDLE_H