commit ff737bdb67a4bae23a9e0de7a653c09a3a76903e Author: Daniel Tsvetkov Date: Mon Jan 17 11:24:32 2022 +0100 Initial commit - play and solve diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..685450e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv +.idea +__pycache__ \ No newline at end of file diff --git a/lib.py b/lib.py new file mode 100644 index 0000000..6bc7f6d --- /dev/null +++ b/lib.py @@ -0,0 +1,16 @@ +DEBUG = False +WORD_LENGTH = 5 +ROUNDS = 6 +INSTRUCTIONS = "Positional letters (green) are CAPITAL, non-positional (yellow) are small, others are blanks e.g. SHe-r" +BLANK = "-" + + +def cat_words(): + with open('/usr/share/dict/words') as f: + for word in f.readlines(): + # remove end of line + word = word[:-1] + # filter out five letter words + if len(word) == WORD_LENGTH and "'" not in word and not word[0].isupper(): + yield word + diff --git a/play.py b/play.py new file mode 100644 index 0000000..f95cb66 --- /dev/null +++ b/play.py @@ -0,0 +1,64 @@ +import random + +from lib import cat_words, INSTRUCTIONS, BLANK, ROUNDS, WORD_LENGTH + +PLAY_INSTRUCTIONS = "Guess a 5-letter word, all lowercase. Hints will be given after the guess. " + INSTRUCTIONS + + +def build_letter_hint(guess, word): + guess_break = [letter for letter in guess] + word_break = [letter for letter in word] + letters_hints = [BLANK] * WORD_LENGTH + for guess_pos, guess_letter in enumerate(guess): + if word[guess_pos] == guess_letter: + letters_hints[guess_pos] = guess_letter.upper() + word_break[guess_pos] = BLANK + guess_break[guess_pos] = BLANK + # see test() - need to be more careful with repeating words + for guess_pos, guess_letter in enumerate(guess_break): + if guess_letter != BLANK and guess_letter in word_break: + letters_hints[guess_pos] = guess_letter + first_index = word_break.index(guess_letter) + word_break[first_index] = BLANK + return ''.join(letters_hints) + + +def main_loop(): + word = random.choice([w for w in cat_words()]) + print(PLAY_INSTRUCTIONS) + for round_number in range(ROUNDS): + print("ROUND {}".format(round_number + 1)) + guess = input("Guess: ") + if len(guess) != 5 and not all([x.islower() for x in guess]): + print("ERROR: {}".format(PLAY_INSTRUCTIONS)) + non_letters_hints = '' + for letter in guess: + if letter not in word and letter not in non_letters_hints: + non_letters_hints += letter + letters_hints = build_letter_hint(guess, word) + print("Non-letters: {}".format(non_letters_hints)) + print("Letters: {}".format(letters_hints)) + print("-" * 10) + if all([x.isupper() for x in letters_hints]): + print('WIN') + break + else: + print("LOSE") + print("Word was: {}".format(word)) + + +def main(): + main_loop() + + +def test(): + assert build_letter_hint('yummy', 'slily') == '----Y' + assert build_letter_hint('yummy', 'slyly') == 'y---Y' + assert build_letter_hint('yymyy', 'slyyy') == 'y--YY' + assert build_letter_hint('ymmyy', 'slyyy') == 'y--YY' + assert build_letter_hint('mmmyy', 'slyyy') == '---YY' + + +if __name__ == "__main__": + test() + main() diff --git a/solve.py b/solve.py new file mode 100644 index 0000000..ed01600 --- /dev/null +++ b/solve.py @@ -0,0 +1,149 @@ +import random + +from lib import cat_words, INSTRUCTIONS, BLANK, ROUNDS, DEBUG + + +def remove_non_letters(word, non_letters): + for non_letter in non_letters: + if non_letter in word: + return True + return False + + +def remove_positional_letters(word, positional_letters): + for pos_idx, positional_letter in enumerate(positional_letters): + if positional_letter == BLANK: + continue + if positional_letter not in word: + return True + if word[pos_idx] != positional_letter: + return True + return False + + +def remove_non_positional_letters(word, non_positional_letters): + for non_pos_idx, non_positional_letter in enumerate(non_positional_letters): + if non_positional_letter == BLANK: + continue + if non_positional_letter not in word: + return True + if word[non_pos_idx] == non_positional_letter: + return True + return False + + +def round_words(non_letters, all_positional_letters, all_non_positional_letters): + for word in cat_words(): + skip = remove_non_letters(word, non_letters) + if skip: + continue + # Find positional letters, e.g. a---- + for positional_letters in all_positional_letters: + skip = remove_positional_letters(word, positional_letters) + if skip: + break + if skip: + continue + # Find non-positional letters, e.g. h-s-- + for non_positional_letters in all_non_positional_letters: + skip = remove_non_positional_letters(word, non_positional_letters) + if skip: + break + if skip: + continue + if not skip: + yield word + + +def first_round_words(): + for word in cat_words(): + word_break = [letter for letter in word] + skip = False + for letter in word_break: + if word_break.count(letter) > 1: + skip = True + break + if skip: + continue + yield word + + +def resolve_positions(letters): + positional_letters, non_positional_letters, is_good = '', '', True + for letter in letters: + if letter == BLANK: + positional_letters += BLANK + non_positional_letters += BLANK + elif letter.isupper(): + positional_letters += letter.lower() + non_positional_letters += BLANK + elif letter.islower(): + positional_letters += BLANK + non_positional_letters += letter + else: + print("ERROR: {}".format(INSTRUCTIONS)) + is_good = False + return positional_letters, non_positional_letters, is_good + + +def main_loop(): + all_non_letters = '' + all_positional_letters = [] + all_non_positional_letters = [] + print(INSTRUCTIONS) + for round_number in range(ROUNDS): + if round_number == 0: + this_round_words = [w for w in first_round_words()] + else: + this_round_words = [w for w in + round_words(all_non_letters, all_positional_letters, all_non_positional_letters)] + if DEBUG: + for word in this_round_words: + print(word) + print("This round words: {}".format(len(this_round_words))) + print("=" * 10) + if len(this_round_words) == 0: + print("ERROR: No known words left.") + break + if DEBUG: + print("ALL Non-letters: {}".format(','.join(all_non_letters))) + print("ALL Positional letters: {}".format(','.join(all_positional_letters))) + print("ALL Non-Positional letters: {}".format(','.join(all_non_positional_letters))) + print("Random word: {}".format(random.choice(this_round_words))) + if len(this_round_words) == 1: + print("That's all I got.") + break + print("=" * 10) + print("ROUND {}".format(round_number + 1)) + print("-" * 10) + while True: + non_letters = input("Non letters: ") + if not all([x.islower() for x in non_letters]): + print("ERROR: {}".format(INSTRUCTIONS)) + continue + break + all_non_letters += non_letters + while True: + letters = input("Letters: ") + if len(letters) != 5: + print("ERROR: {}".format(INSTRUCTIONS)) + continue + positional_letters, non_positional_letters, is_good = resolve_positions(letters) + if is_good: + all_positional_letters.append(positional_letters) + all_non_positional_letters.append(non_positional_letters) + break + if all([letter.isupper for letter in letters]): + print("WIN") + else: + print("Sorry. Remaining words: {}".format(len(this_round_words))) + for word in this_round_words: + print(word) + + +def main(): + main_loop() + + +if __name__ == "__main__": + main()