add wordle
Some checks failed
Release / check_for_changed_version (push) Has been cancelled
Release / build (push) Has been cancelled

This commit is contained in:
Joe Kaufeld 2025-09-18 11:56:58 -04:00
parent 55c3307b74
commit 7943467f1c
2 changed files with 186 additions and 5 deletions

View file

@ -15,6 +15,7 @@ from shiv.bootstrap import current_zipfile
import src import src
from src.helpers import flip_char, load_config, write_config from src.helpers import flip_char, load_config, write_config
from src.joplin import process_joplin_posts, get_folders, BASE_URL from src.joplin import process_joplin_posts, get_folders, BASE_URL
from src.wordle import wordle
from src.art import BANNERS from src.art import BANNERS
@ -27,12 +28,12 @@ from src.art import BANNERS
@click.option( @click.option(
"--update", "--update",
is_flag=True, is_flag=True,
help="Check Gitea for a new version and auto-update.", help="Check Forgejo for a new version and auto-update.",
) )
def main(ctx, update): def main(ctx, update):
"""Launch a utility or drop into a command line REPL if no command is given.""" """Launch a utility or drop into a command line REPL if no command is given."""
if update: if update:
update_from_gitea() update_from_forgejo()
sys.exit() sys.exit()
elif ctx.invoked_subcommand is None: elif ctx.invoked_subcommand is None:
banner = random.choice(BANNERS) banner = random.choice(BANNERS)
@ -208,8 +209,8 @@ def get_saved_from_reddit() -> None:
_process(item) _process(item)
def update_from_gitea(): def update_from_forgejo():
"""Get the newest release from Gitea and install it.""" """Get the newest release from Forgejo and install it."""
status = Status("Checking for new release...") status = Status("Checking for new release...")
status.start() status.start()
@ -219,7 +220,7 @@ def update_from_gitea():
if response.status_code != 200: if response.status_code != 200:
status.stop() status.stop()
click.echo( click.echo(
f"Something went wrong when talking to Gitea; got a" f"Something went wrong when talking to Forgejo; got a"
f" {response.status_code} with the following content:\n" f" {response.status_code} with the following content:\n"
f"{response.content}" f"{response.content}"
) )
@ -243,6 +244,7 @@ def update_from_gitea():
status.stop() status.stop()
click.echo(f"Updated to {release_data['tag_name']}! 🎉") click.echo(f"Updated to {release_data['tag_name']}! 🎉")
main.add_command(wordle)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

179
src/wordle.py Normal file
View file

@ -0,0 +1,179 @@
import random
import click
import httpx
from rich.status import Status
from src.helpers import base_folder
all_valid_guesses = "https://gist.githubusercontent.com/itsthejoker/7d4c0d10a4e97cc7493e765d9e848a53/raw/b3970cc79bf88c872dd67c3896d8a8673377b0fd/wordle-nyt-allowed-guesses-update-12546.txt"
all_answers = "https://gist.githubusercontent.com/itsthejoker/1c7b8b97c454ce464aa5ea7f187d81e9/raw/1a2e59929b833925af800298d1003e48d90b5898/wordle-nyt-answers-alphabetical.txt"
answers_path = base_folder / "opsbox_wordle_answers.txt"
guesses_path = base_folder / "opsbox_wordle_guesses.txt"
def download_wordle_data():
with httpx.Client() as client:
with open(answers_path, "w") as f:
f.write(client.get(all_answers).text)
with open(guesses_path, "w") as f:
f.write(client.get(all_valid_guesses).text)
def check_if_need_to_download_data():
if not answers_path.exists() or not guesses_path.exists():
download_wordle_data()
def load_data():
status = Status("Setting up game...")
status.start()
check_if_need_to_download_data()
with open(answers_path, "r") as f:
answers = f.read().splitlines()
with open(guesses_path, "r") as f:
guesses = f.read().splitlines()
guesses += answers # answers are valid guesses
status.stop()
return answers, guesses
def print_keyboard(guessed_word_list: list[str], keyboard_display: dict[str, int]):
first_row = "qwertyuiop"
second_row = "asdfghjkl"
third_row = "zxcvbnm"
click.echo()
all_letters_guessed = set("".join(guessed_word_list))
for count, item in enumerate([first_row, second_row, third_row]):
click.echo(" " * count if count != 2 else " " * 3, nl=False)
for letter in item:
click.echo(" ", nl=False)
if letter in all_letters_guessed and keyboard_display.get(letter) == 2:
click.echo(
click.style(letter.capitalize(), fg="green", bold=True), nl=False
)
elif letter in all_letters_guessed and keyboard_display.get(letter) == 1:
click.echo(
click.style(letter.capitalize(), fg="yellow", bold=True), nl=False
)
elif letter in all_letters_guessed:
click.echo(
click.style(letter.capitalize(), fg="white", bold=True), nl=False
)
else:
click.echo(click.style(letter.capitalize(), fg="white"), nl=False)
click.echo("\n")
def print_word_matrix(
answer: str,
letter_counts: dict[str, int],
guessed_word_list: list[str],
keyboard_display: dict[str, int],
) -> None:
for guess in guessed_word_list:
guess_letter_counts = {}
click.echo(" ", nl=False)
for count, letter in enumerate(guess):
add_newline = count == 4
guess_letter_counts[letter] = guess_letter_counts.get(letter, 0) + 1
if letter == answer[count]:
click.echo(
click.style(letter.capitalize(), fg="green", bold=True),
nl=add_newline,
)
keyboard_display[letter] = 2
elif (
letter in answer
and guess_letter_counts[letter] <= letter_counts[letter]
):
click.echo(
click.style(letter.capitalize(), fg="yellow", bold=True),
nl=add_newline,
)
if keyboard_display.get(letter) != 2:
keyboard_display[letter] = 1
elif (
letter in answer and guess_letter_counts[letter] > letter_counts[letter]
):
click.echo(
click.style(letter.capitalize(), fg="white", bold=True),
nl=add_newline,
)
else:
click.echo(
click.style(letter.capitalize(), fg="white", bold=True),
nl=add_newline,
)
if len(guessed_word_list) < 6 and answer not in guessed_word_list:
click.echo()
click.echo("You have ", nl=False)
click.echo(
click.style(str(6 - len(guessed_word_list)), fg="white", bold=True),
nl=False,
)
click.echo(f" guess{'es' if len(guessed_word_list) != 4 else ''} left.")
def get_unique(sequence):
seen = set()
return [x for x in sequence if not (x in seen or seen.add(x))]
@click.command(help="Start a new game of Wordle!")
@click.option("--debug", is_flag=True, help="Debug mode")
def wordle(debug):
click.echo()
click.echo("Starting a new game of Wordle!")
click.echo("You have six tries to find a 5 letter word.")
click.echo(
"Gray letters are not in the word, yellow letters are in the word but in the"
" wrong place, and green letters are in the right place."
)
click.echo("Good luck!")
click.echo()
answers, guesses = load_data()
todays_word = random.choice(answers)
# every key is a letter, the value is 1 if it's yellow and 2 if it's green
keyboard_display = {}
if debug:
click.echo(f"Today's word is {todays_word}")
guessed_words = []
letter_set = get_unique(todays_word)
letter_counts = {letter: todays_word.count(letter) for letter in letter_set}
while True:
if len(guessed_words) == 6:
click.echo("You lose!")
click.echo("The word was: ", nl=False)
click.echo(click.style(str(6 - len(todays_word)), fg="white", bold=True))
click.echo("Better luck next time!")
break
guess = click.prompt("Enter a guess")
guess = guess.lower()
click.echo()
if len(guess) < 5 or len(guess) > 5:
click.echo("Guesses must be 5 letters.")
continue
if guess not in guesses:
click.echo("Sorry, that's not a valid guess. Try again!")
continue
guessed_words += [guess]
print_word_matrix(todays_word, letter_counts, guessed_words, keyboard_display)
print_keyboard(guessed_words, keyboard_display)
click.echo()
if guess == todays_word:
click.echo("You win!")
break