3
\$\begingroup\$

rules

Unworld is a word guessing game in which it shows you a n*n color grid and a base word and the goal of the player is to fill words into the grid such that it follows these rules:

  • a green brick must contain a letter from the base word with correct positioning.
  • a yellow brick must contain a letter from the base word but in an incorrect position
  • a gray brick must NOT contain ANY letter from the base word
  • a level beneath another level must use all the information from the level above it:
    • so: if a yellow or green letter is used on an higher level they must be used in a lower level
    • if a gray letter is used on an higher level they must NOT be used on the lower level
    • a yellow letter can not be used on the same index multiple times
    • if it is known that there's only M amount of letter k, you can not use more than that amount in you building

example:

example starting position example solution
image on the left is the starting position.
image on the right is the winning position.
they follow all the rules mentioned above.

problem I wrote a code to solve the game and it can solve 80% of the puzzle with no sweat, but it get really slow as the question become more difficult. I am not sure if it was due to the algorithm or the flow of the program. The code was provided below.

link to the dictionary file I use


unwordle.py

def help_() -> None:
    """message for help/-help flags."""

    s = """unwordle is a popular word game!
learn more about them here:https://unwordle.org/?daily=1
this program is a game solver.
first check out the unwordle game board.
then in command line type in `python unwordle.py `
if the first row is *green* *yellow* *gray* *gray* *gray*,
you would add `GYXXX` at the end of that command
you repeat this for the next two row.(all the hints)
and lastly typed in the answer word (letter only)
a sample output would look something like this:
`python unwordle.py YXXXX XYXYX XGYXG ralph\n`"""

    print(s)
    exit()
    return


def check_sys_arg(sys_argv: list) -> list:
    """
validating the input of the user.
<br>including checking if word is the right len
<br>and if contains disallowed characters.
  """

    
    ERROR_MSG_2 = ("include disallowed character in hint color:"+
"`G for green, Y for yellow, and X for gray`")
    ERROR_MSG_3 = "include disallowed character in answer word`"
    ERROR_MSG_4 = "inconsistent word length in input"

    ALLOWED_COLOR_S: set = set("GYX")
    ALLOWED_CHR_S: set = set("abcdefghijklmnopqrstuvwxyz")

    output = []

    wrd_ln = len(sys_argv[1])

    for x in range(1, len(sys_argv) - 1):
        x = sys_argv[x].upper()
        if set(x) - ALLOWED_COLOR_S:
            print("use `unwordle.py -help` to get help")
            raise Exception(ERROR_MSG_2)
        if len(x) != wrd_ln:
            print("use `unwordle.py -help` to get help")
            raise Exception(ERROR_MSG_4)
        output.append(x)

    x = sys_argv[-1].lower()
    if set(x) - ALLOWED_CHR_S:
        print("use `unwordle.py -help` to get help")
        raise Exception(ERROR_MSG_3)
    if len(x) != wrd_ln:
        print("use `unwordle.py -help` to get help")
        raise Exception(ERROR_MSG_4)
    output.append(x)

    return output


def import_dictionary(file: str = "dictionary.txt",
                      target_len_int: int = 5) -> list:
    """
reading and filtering words from txt files.
<br>file: the directory of which txt file is located
<br>target_len_int : target word length
  """
    output = []
    with open(file, "r+") as f:  #reading list of words
        temp = f.readlines()
        for word in temp:
            word = word.strip()
            if len(word) == target_len_int:
                output.append(word)
    return output


def color_word(answer_word: str, guess_word: str) -> str:
    """
take two words and color accordingly.
an example output would be "XXYYG"
  """
    color_l = ["X"] * len(guess_word)
    ans_wrd_l = list(answer_word)
    for counter, (chr_1, chr_2) in enumerate(zip(answer_word, guess_word)):
        if chr_1 == chr_2:
            color_l[counter] = "G"
            ans_wrd_l.remove(chr_1)
    for counter, chr_1 in enumerate(guess_word):
        if color_l[counter] == "G":
            continue
        if chr_1 in ans_wrd_l:
            color_l[counter] = "Y"
            ans_wrd_l.remove(chr_1)
    return "".join(color_l)


def find_candidate(wrd_l: list, final_ans: str, search: str) -> dict:
    """
loop through a word list and search for words that fit the criteria.
<br>the result to the function is a dict with:
<br>key being the word, val being a tuple of two set.:
<br>explain below:
<br>the first set contain sucess guesses(Y/G)
<br>the second set contain failed guesses:
<br>X or Y that is out of position
  """
    def multi_apperance(chr: str,
                        succes: set,
                        append_to: set,
                        break_: bool = True):
        for x in range(1, len(o) + 1):
            x = str(x) + chr
            if x not in success:
                append_to.add(x)
                if break_:
                    break

    output = {}
    for o in all_wrd_l:
        if color_word(final_ans, o) != search:
            continue

        fail = set()
        success = set()

        for counter, (chr, color) in enumerate(zip(o, search)):
            if color == "X":
                continue

            if chr in success:
                multi_apperance(chr, success, success)
            else:
                success.add(chr)

            if color == "Y":
                fail.add(counter * "X" + chr)

        for counter, (chr, color) in enumerate(zip(o, search)):
            if color != "X":
                continue
            if chr not in success:
                fail.add(chr)
            else:
                fail.add(counter * "X" + chr)
                multi_apperance(chr, success, fail, False)

        output[o] = (success, fail)
    return output


def main(cand_list: list, answer: str):
    """
collect a list of possible candidates and render result. always return 0.
<br>required args: 
<br>cand_list -> the candidate list. for reference only. 
<br>include a list of all possible candidate dict here.
<br>starting from clue 1 ...clue 3
<br> answer -> final answer
  """


  
    INSTRUCTION_MSG = """want a different answer?
enter 1 for a different answer or enter 0 to exit the program.
tip: you can enter any number and the word at that index will be changed
the left most word has a index of 1.
    """
    def askinput():
      print(INSTRUCTION_MSG)  
      while True:
        ask_input = input("your respond:")
        print()
        if not ask_input:
            ask_input = "1"
        ask_input = ask_input.strip()
        if set(ask_input) - set("1234567890"):
            print("invalid input, let's try again.")
        else:
            return ask_input
    def inner_main(depth: int,
                   cand_list: list,
                   answer: str,
                   display: list = [],
                   success_j: set = {},
                   fail_j: set = {}) -> 0:
        """The worker function for main."""

        i_cands = cand_list[depth]

        for cand in i_cands:
            success_i, fail_i = i_cands[cand]
            if display:
                if success_i - success_j:
                    continue
                if fail_i.intersection(fail_j):
                    continue
            display.insert(0, cand)

            if depth:
                _ = inner_main(depth - 1, cand_list, answer, display,
                               success_i, fail_i.union(fail_j))
            else:
                print("answer suggestion:")
                display.append(answer)
                print(display)

                _ = askinput()
              
                _ = int(_) - 1
                del display[-1]
            del display[0]
            if _:
                return _ - 1

        return 0

    inner_main(len(cand_list) - 1, cand_list, answer)
    return 0


if __name__ == "__main__":
    import sys
    print()  # print an extra line to avoid any confusion
    if len(sys.argv) == 1:
        print("use `unwordle.py -help` to get help")
        print()
        exit()
    help_flags = ["h", "help", "-help", "-h", "--help", "--h"]
    if sys.argv[1].lower() in help_flags:
        help_()
        print()
    if len(sys.argv) == 2:
        print("use `unwordle.py -help` to get help")
        print()
        exit()
    user_input_l: list = check_sys_arg(sys.argv)

    WRD_LN = len(user_input_l[0])
    ANSWER = user_input_l.pop()
    GUESSES = len(user_input_l)

    all_wrd_l: list = import_dictionary(target_len_int=WRD_LN)

    cand = []
    for clues in user_input_l:
        temp: dict = find_candidate(all_wrd_l, ANSWER, clues)
        cand.append(temp)

    main(cand, ANSWER)
\$\endgroup\$
0

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.