====== cs20pf22as14: A Wordle Guesser! ======
===== Goals =====
* Develop some logic to successfully solve Worldle-like puzzles.
* Learn a bit about interacting with a simple stateful web service from Python.
-----
====== Prerequisites ======
This assignment requires familiarity with the [[:lecture materials/]] presented in class through [[:lecture materials/week 14]].
-----
====== Background ======
===== Wordle =====
{{:wordle_example.png?nolink|Image depicting a Wordle game that was won in four guesses}}
You may be familiar with [[https://www.nytimes.com/games/wordle/index.html|Wordle]], which was just someone's little pet project that became very popular very quickly in late 2021. Quoth the [[https://en.wikipedia.org/wiki/Wordle|arbiter of all human information]]:
> Players have six attempts to guess a five-letter word, with feedback given for each guess in the form of colored tiles indicating when letters match or occupy the correct position. The mechanics are nearly identical to the 1955 pen-and-paper game Jotto and the television game show franchise Lingo. Wordle has a single daily solution, with all players attempting to guess the same word.
To the right, you can see a visual depiction of what it looks like to play Wordle. In this example, the player guessed the correct word in four tries. Each guess is given feedback as described on Wikipedia:
> After every guess, each letter is marked as either green, yellow or gray: green indicates that letter is correct and in the correct position, yellow means it is in the answer but not in the right position, while gray indicates it is not in the answer at all. Multiple instances of the same letter in a guess, such as the "o"s in "robot", will be colored green or yellow only if the letter also appears multiple times in the answer; otherwise, excess repeating letters will be colored gray.
===== Wordle Web Service on jeff.cis.cabrllo.edu ====
There is a stateful web service at URL https://jeff.cis.cabrillo.edu/util/wordle that provides an API for playing a Wordle-like game. Visiting that URL in a browser will show you a short message. The service is //stateful// in the sense that it keeps track of the state of a user's game (and history of games) on the server side. When a client "logs in" by sending a user ID, the server will respond with a [[https://en.wikipedia.org/wiki/HTTP_cookie|cookie]] that allows the client to stay "logged in" as it plays the game, in the same way that you remain "logged into" this website you're using right now. This is the same mechanism that keeps you logged into most other services like Twitter, Gmail, etc., when you're using them through a web browser.
Your user ID for this service is: hello
Words are chosen randomly from file ''[[http://jeff.cis.cabrillo.edu/datasets/wordle_words.txt|/srv/datasets/wordle_words.txt]]'', which contains 18455 five-letter words, one per line.
Communicating with this service is made easy by the ''[[https://requests.readthedocs.io/en/latest/|requests]]'' module, where a simple Python ''dict'' is the means for sending request data (e.g. logging in and making a guess) to the server and receiving responses from the server (indicating the progress of the game)
>>> import requests
>>> URL = 'https://jeff.cis.cabrillo.edu/util/wordle'
>>> USER_ID = '0123456789abcdef0123456789abcdef01234567' # this dummy user id works for demo purposes
>>> session = requests.Session() # will automatically deal with cookies
>>> # logs in and tells the server to reset all records of wins/losses for this user:
>>> response = session.post(URL, {'user_id': USER_ID, 'reset_statistics': True})
>>> print(response.json()) # method json() converts the response to a Python dict
{'message': 'Welcome, user 0123456789abcdef0123456789abcdef01234567! Never seen you before... 😉'}
>>> response = session.post(URL, {'guess': 'APPLE'})
>>> print(response.json())
{'progress': [False, None, None, None, None], 'guesses': 1}
The response to a ''guess'' request consists of a ''dict'' in either of the following forms:
- **If a game is in progress** (i.e. fewer than 6 guesses, and the word has not been guessed):
- Key ''progress'' has a ''list'' of five values describing the feedback for each character in the guess: ''True'' is equivalent to a green tile in Wordle, ''False'' is equivalent to a yellow tile, and ''None'' is equivalent to a gray tile.
- Key ''guesses'' indicates how many guesses have been made in the game (''1''–''5'').
- **If a game has ended** (i.e. the word has been guessed, or there have been six incorrect guesses):
- Key ''progress'' is the same as previously.
- Key ''guesses'' will have the value ''6''.
- Key ''word'' indicates what the correct word was.
- Key ''total_games'' indicates the total number of games the user has played.
- Key ''wins'' indicates the number of games the user has won.
Here is a screenshot of me manually replicating the Wordle game illustrated in the image above through this web service:
{{:wordle_requests_example.png?nolink|Screenshot of a replication of the four-guess World game above using the requests module to communicate with our Wordle web service}}
-----
====== Assignment ======
You shall ''def''ine a ''class'' named ''WordlePlayer'' in a module named ''wordle'' with the following attributes:
- A constructor that sends a login request to the Wordle service on ''jeff.cis.cabrillo.edu''.
- A method named ''play()'' that plays one game of Wordle, attempting to guess the correct word.
If you'd like to print any status messages etc., that's fine. They will be ignored.
===== Starter Code =====
Feel free to start with this code. For now it is an interface for a human to play a Wordle game. At minimum you need to change the ''play()'' method to play automatically instead of use user input for the guesses.
""" Interacting with the Wordle web service on jeff.cis.cabrillo.edu. """
import requests
from colorama import Fore, Back, Style
URL = 'https://jeff.cis.cabrillo.edu/util/wordle' # Might as well keep this around
USER_ID = '0123456789abcdef0123456789abcdef01234567' # Change this
def _load_words():
"""Returns the Wordle dictionary file as a set of strings."""
with open('/srv/datasets/wordle_words.txt') as word_file:
all_words = set(map(str.rstrip, word_file))
return all_words
DICTIONARY = _load_words()
class WordlePlayer:
"""I try to win Wordle games!"""
def __init__(self):
"""Logs into the Wordle service on jeff.cis.cabrillo.edu."""
self._session = requests.Session()
self._session.post(URL, {'user_id': USER_ID})
# TODO (as desired)
def play(self):
"""Plays a game from start to finish, attempting to guess the word."""
# TODO (for now plays interactive with user input; change to have it play automatically!)
guess = input()
response = self._session.post(URL, {'guess': guess}).json()
print(''.join(Fore.WHITE + (Back.BLACK if r is None else Back.GREEN if r else Back.YELLOW) + c
for (c, r) in zip(guess, response['progress'])) + Style.RESET_ALL)
while 'word' not in response:
guess = input()
response = self._session.post(URL, {'guess': guess}).json()
print(''.join(Fore.WHITE + (Back.BLACK if r is None else Back.GREEN if r else Back.YELLOW) + c
for (c, r) in zip(guess, response['progress'])) + Style.RESET_ALL)
if all(response['progress']):
print('✅')
else:
print(f'🔴 ({response["word"]})')
if __name__ == '__main__':
player = WordlePlayer()
player.play()
===== Leaderboard =====
As submissions are received, this leaderboard will be updated with the top-performing solutions, with regard to win rate (as long as they're not too slow to test).
Rank | Win Rate | Test Time (s) | Memory Usage (kB) | SLOC (lines) | User |
-----
====== Submission ======
Submit ''wordle.py'' via [[info:turnin]].
{{https://jeff.cis.cabrillo.edu/images/feedback-robot.png?nolink }} //**Feedback Robot**//
This project has a feedback robot that will run some tests on your submission and provide you with a feedback report via email within roughly one minute.
Please read the feedback carefully.
====== Due Date and Point Value ======
Due at 23:59:59 on the date listed on the [[:syllabus|syllabus]].
''Assignment 14'' is worth **up to 100 points of extra credit**. Your grade will be based on your percentage win rate, so grades will range from 0 to 100.
Possible point values per category:
---------------------------------------
Win rate for your player! 100
Possible deductions:
Style and practices 10–20%
Possible extra credit:
Submission via Git 5%
---------------------------------------