diff --git a/.gitignore b/.gitignore index 685450e..18d22ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ venv .idea -__pycache__ \ No newline at end of file +__pycache__ +sensitive.py +*.pyc \ No newline at end of file diff --git a/lib.py b/lib.py index 61e9f41..708c080 100644 --- a/lib.py +++ b/lib.py @@ -1,3 +1,5 @@ +import string + DEBUG = False WORD_LENGTH = 5 ROUNDS = 6 @@ -16,5 +18,9 @@ def cat_words(dictionary=DICTIONARY): # remove end of line word = word[:-1] # filter out five letter words - if len(word) == WORD_LENGTH and all([letter.isalpha() and letter.islower() for letter in word]): + if len(word) == WORD_LENGTH and all([letter in string.ascii_lowercase for letter in word]): yield word + + +if __name__ == "__main__": + print(len(list(cat_words()))) \ No newline at end of file diff --git a/play.py b/play.py index 6440960..09638d3 100644 --- a/play.py +++ b/play.py @@ -22,6 +22,7 @@ def build_letter_hint(guess, word): word_break[first_index] = BLANK return ''.join(letters_hints) + def build_non_letter_hint(guess, word): non_letters_hints = '' for letter in guess: @@ -29,6 +30,7 @@ def build_non_letter_hint(guess, word): non_letters_hints += letter return non_letters_hints + def main_loop(): word = random.choice([w for w in cat_words()]) print(PLAY_INSTRUCTIONS) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b019c33 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +click==8.0.3 +Flask==2.0.2 +itsdangerous==2.0.1 +Jinja2==3.0.3 +MarkupSafe==2.0.1 +Werkzeug==2.0.2 diff --git a/solve.py b/solve.py index 16e2886..bca6c72 100644 --- a/solve.py +++ b/solve.py @@ -3,22 +3,24 @@ import random from lib import cat_words, INSTRUCTIONS, BLANK, ROUNDS, DEBUG, DICTIONARY -def remove_non_letters(word, non_letters): +def remove_non_letters(word, non_letters, unconsumed_letters): for non_letter in non_letters: - if non_letter in word: + if non_letter in word and non_letter in unconsumed_letters: return True return False -def remove_positional_letters(word, positional_letters): +def remove_positional_letters(word, positional_letters, ): + rv, unconsumed_letters = False, '' for pos_idx, positional_letter in enumerate(positional_letters): if positional_letter == BLANK: + unconsumed_letters += word[pos_idx] continue if positional_letter not in word: - return True - if word[pos_idx] != positional_letter: - return True - return False + rv = True + elif word[pos_idx] != positional_letter: + rv = True + return rv, unconsumed_letters def remove_non_positional_letters(word, non_positional_letters): @@ -34,12 +36,10 @@ def remove_non_positional_letters(word, non_positional_letters): def round_words(non_letters, all_positional_letters, all_non_positional_letters, dictionary=DICTIONARY): for word in cat_words(dictionary): - skip = remove_non_letters(word, non_letters) - if skip: - continue + skip = False # Find positional letters, e.g. a---- for positional_letters in all_positional_letters: - skip = remove_positional_letters(word, positional_letters) + skip, unconsumed_letters = remove_positional_letters(word, positional_letters) if skip: break if skip: @@ -49,6 +49,9 @@ def round_words(non_letters, all_positional_letters, all_non_positional_letters, skip = remove_non_positional_letters(word, non_positional_letters) if skip: break + if skip: + continue + skip = remove_non_letters(word, non_letters, unconsumed_letters) if skip: continue if not skip: @@ -142,18 +145,26 @@ def main_loop(): print(word) -def test(): +def find_word(search): + for i, word in enumerate(cat_words()): + if word == search: + print(i) + + +def do_test(): assert [w for w in round_words('', ['----y'], ['-----'], dictionary=['slily'])] == ['slily'] assert [w for w in round_words('', ['----y'], ['y----'], dictionary=['slyly'])] == ['slyly'] assert [w for w in round_words('', ['---yy'], ['y----'], dictionary=['slyyy'])] == ['slyyy'] assert [w for w in round_words('', ['---yy'], ['-----'], dictionary=['slyyy'])] == ['slyyy'] assert [w for w in round_words('', ['-y---'], ['--y--'], dictionary=['sysys'])] == ['sysys'] + assert [w for w in round_words('nu', ['-o-ns'], ['-----'], dictionary=['towns'])] == ['towns'] def main(): - test() + do_test() main_loop() if __name__ == "__main__": + # find_word('towns') main() diff --git a/templates/game.html b/templates/game.html new file mode 100644 index 0000000..3cd2910 --- /dev/null +++ b/templates/game.html @@ -0,0 +1,22 @@ +{% include "layout.html" %} +{% block body %} + home +
+ + {% if state_playing %} +
+ + +
+ {% else %} +

{{ state }}

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..34dc500 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,9 @@ +{% include "layout.html" %} +{% block body %} +Random game +
+ + + +
+{% endblock %} \ No newline at end of file diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 0000000..2345e0e --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,47 @@ + + + + + pi2-wordle + + + +{% with messages = get_flashed_messages() %} + {% if messages %} + + {% endif %} +{% endwith %} +{% block body %}{% endblock %} + + \ No newline at end of file diff --git a/web.py b/web.py new file mode 100644 index 0000000..6e06daa --- /dev/null +++ b/web.py @@ -0,0 +1,133 @@ +import random +from string import ascii_lowercase + +from flask import Flask, render_template, session, request, redirect, url_for, flash + +import sensitive +from lib import cat_words, ROUNDS +from play import build_non_letter_hint, build_letter_hint + +app = Flask(__name__) +app.config['SECRET_KEY'] = sensitive.SECRET_KEY +NUM_GAMES = 4593 +GUESS_SPLITTER = '|' +STATE_PLAYING = 'PLAYING' +STATE_WIN = 'WIN' +STATE_LOSE = 'LOSE' + + +@app.route('/') +def home(): + num_games = NUM_GAMES # len(list(cat_words())) - 1 + return render_template('home.html', num_games=num_games) + + +def init_game(word): + session['word'] = word + session['guesses'] = '' + session['state'] = STATE_PLAYING + + +@app.route('/init/random') +def init_random_game(): + word = random.choice([w for w in cat_words()]) + session['game_id'] = -1 + init_game(word) + return redirect(url_for('random_game')) + + +@app.route('/init/deterministic', methods=['post']) +def init_deterministic_game(): + game_id = request.form.get('game_id') + if not game_id.isdigit() or not 0 < int(game_id) < NUM_GAMES: + flash("game_id must be between 0 and {}".format(NUM_GAMES)) + return redirect(url_for('home')) + return redirect(url_for('init_deterministic_game_id', game_id=game_id)) + + +@app.route('/init/deterministic/') +def init_deterministic_game_id(game_id): + word = [w for w in cat_words()][int(game_id)] + session['game_id'] = game_id + init_game(word) + return redirect(url_for('deterministic_game_id', game_id=game_id)) + + +def resolve_guesses(): + word = session['word'] + guesses = session.get('guesses', '').split(GUESS_SPLITTER) + guesses_colored = [] + for guess in guesses: + non_letters_hints = build_non_letter_hint(guess, word) + letters_hints = build_letter_hint(guess, word) + colored_guess = [] + for pos, letter in enumerate(guess): + if letter in non_letters_hints: + color = 'grey' + elif letters_hints[pos] == letter.upper(): + color = 'green' + elif letters_hints[pos] == letter: + color = 'yellow' + else: + color = 'grey' + colored_guess.append((letter, color)) + guesses_colored.append(colored_guess) + return guesses_colored + + +def build_game_state(): + guesses_colored = resolve_guesses() + return render_template('game.html', + guesses=guesses_colored, + state_playing=session['state'] == STATE_PLAYING, + state=session['state']) + + +@app.route('/random') +def random_game(): + if 'word' not in session or 'state' not in session: + return redirect(url_for('init_random_game')) + return build_game_state() + + +@app.route('/deterministic/') +def deterministic_game_id(game_id): + if 'word' not in session or 'state' not in session: + return redirect(url_for('init_deterministic_game_id', game_id=game_id)) + return build_game_state() + + +@app.route('/guess', methods=['post']) +def make_guess(): + if 'word' not in session or 'guesses' not in session: + flash('need a word') + return redirect(url_for('home')) + guess = request.form.get('guess') + if len(guess) != 5 and not all([x in ascii_lowercase for x in guess]): + flash("Make a 5 letter guess.") + return build_game_state() + if guess not in cat_words(): + flash('Not in word list') + return build_game_state() + word = session['word'] + round = len(session['guesses'].split(GUESS_SPLITTER)) + if round >= ROUNDS: + session['state'] = STATE_LOSE + if guess == word: + session['state'] = STATE_WIN + session['guesses'] += '{}{}'.format(guess, GUESS_SPLITTER) + if 'game_id' not in session: + flash('no game_id') + return redirect(url_for('home')) + game_id = session['game_id'] + if game_id == -1: + return redirect(url_for('random_game')) + return redirect(url_for('deterministic_game_id', game_id=game_id)) + + +def main(): + app.run(debug=True) + + +if __name__ == "__main__": + main()