Appearance
Assignment 5: WordGame
PrairieLearn Quiz Due: October 28, 2025 (no grace day)
Assignment Due: October 30, 2025 (has one grace day)
Overview
This assignment is about implementing a single-player, turn-based word game of your own creation. Your goal is to make a word game that one person can play in the terminal/console. You can base your game on an existing idea (like New York Times Wordle and Spelling Bee) or make something completely new. You will decide how your game looks, how the player gives input, and how the game decides who wins or earns points. Do not worry, there will still be clear steps to follow that will help you complete the project.
What Makes This Assignment Different
Unlike previous assignments, this one doesn’t have a specific “right answer” that your code should implement. This openness is intentional—it’s an opportunity to apply what you’ve learned creatively and show your problem-solving process.
You will first write a set of required helper functions that make building your game easier. Most of these functions are underspecified, meaning their requirements do not always specify exactly what they should return (or even what their parameters are). Instead, you get to decide not only how the required functions are implemented, but also what they do (while still satisfying the requirements).
Learning Goals
- Use while-loops where for-loops are less suitable (e.g., where the number of iterations cannot be predetermined).
- Use the game loop pattern (defined in the next subsection).
- Obtain and validate user inputs.
- Process file contents.
- Practice string manipulations.
- Decompose a large program into separate helper functions (across multiple files).
- Document what your functions do and provide example usage.
- Practice the 7-steps, where steps 1-4 are often the harder part.
Game State and the Game Loop Pattern
The game state and game loop pattern are two important concepts that you'll use throughout this assignment.
The game state consists of variables and data needed to describe all parts of the game at any one point in time. You might think of it as the data required to restore the game back to things as they were before the machine was abruptly powered off so that the player could resume playing as if it never turned off and was turned back on. For example, in the board game Monopoly, the game state would include identifying information about each player, their turn order, which player's turn it is, all entities (properties, hotels, money, "Get Out of Jail Free" cards) owned by each player, the position of their pieces on the board.
How to represent and store the game state is up to you as the designer. In principle, you could use a single variable named state that is a list of all relevant information, possibly containing other collections (lists, sets, tuples, etc.) within it. However, we strongly recommend against this, and suggest using any number of appropriately-named variables to keep track of individual parts of the state. This is a notion similar to using helper functions that are called from a main function (and each other) instead of having only one large, monolithic function.
Over the course of a game, the state is likely to change, either in response to player interactions or the passage of time—think of real-time video games where things happen even if the player lets go of their controller. Since this assignment is about single-player turn-based games in the console/terminal, we focus only on updating state in response to each of the player's turn (but the game state pattern is also used in real-time games).
The game loop pattern is similar to the accumulator pattern and is used to update the game state. It consists of three main parts:
- a setup step that initializes variables that represent the game state,
- a game loop (typically a while-loop) that:
- waits for player input,
- processes the player input, then
- updates the game state in light of the input, and
- a teardown step to handle any final processing at game end before terminating.
In Python, this might look like the following (printing and arguments not shown):
python
# Initialize game state and print initial info to player
setup(...)
while not GAME_OVER:
# How to present info to the player with print() and input() is up to you!
... = displayGameState(...)
# Not all inputs are valid in most games.
# For this assignment, you'll insist players give valid inputs by
# reprompting if they don't.
... = validUserInput(...)
# If your function(s) to process the input and update the game state
# *mutate* the arguments, then assigning its return value may not
# be necessary.
... = updateGameState(...)
# Don't terminate abruptly; first display final info after any additional processing.
teardown(...)Getting Started
Download the starter code. The zip file contains three template files and three complete modules to be used as tools.
Files to Modify and Submit
MyWordGame.py: This module includes minimal starter code and where you will implement your own word game. At the top of the file, we have already includedimport WGLibfor you, and at the bottom of the file, we have provided a basic main block for you to use and/or expand on when testing your code. This module is responsible for implementing using the game loop pattern for your game in Part 2.WGLib.py: This module contains the word game library functions to be implemented as specified in Part 1. In order to enable you to implement any game you wish, most of these library functions are intentionally underspecified. They have general-purpose names and partial docstrings that describe what they should be used for at a high-level, but exactly what they do, how they do it, and when/where you call them from another is up for you to decide.RULES.md: This is a Markdown file (extension.md) that will be used to generate a user-friendly webpage to host your game in an online gallery after the assignments are turned in. Besides giving your game a name, there are three sections to complete to explain how to play your game, any settings to be chosen by a player, and a short description of your game in a few sentences.only_valid_one_play.jsonandsome_invalid_multiple_plays.json: These are JSON (Javascript Object Notation) files which are created by running the RecordMyRun.py module for Part 4 of the assignment. They will be used by the autograder to “replay” through the game by following your recorded inputs.About Markdown
For the purposes of this assignment, you can treat
RULES.mdlike a basic plaintext file by writing text under the appropriate "headers" (these begin with some number of#s). For those interested in adding emphasis by way of italics, bold, etc., some basic Markdown syntax is described here: Markdown Guide.PyCharm should display a "preview" of the HTML described by your Markdown file when it is open in the editor (but with less styling than on this site). More details about PyCharm's Markdown Preview are here.
Provided Complete Files
lowerwords.txt: A flat file containing ~46k English words, one per line, without any extra whitespace.RecordMyRun.py: Running this module simply runs your game, but all strings input by the player are recorded in the process and written to a "run" file. These recorded runs allow Gradescope's autograder to "replay" the run and check for various behavior.ReplayMyRun.py: Running this module allows you to "replay" a run recorded withRecordMyRun.py.DukeGPTHints.py: This module allows you to use DukeGPT's Mistral model to generate short, crossword-style hints for words on-demand. Using this module is entirely optional but is a great way to provide the players word-related hints! For more details, see the corresponding section in the Appendix.
Assignment Steps
This assignment involves submitting five different files, described in Parts 1–4. Note there is a quiz on PrairieLearn to do as well.
- Part 0: Sketch out the rules of your game in
RULES.mdand lay out a plan for implementing it. First focus on what the main steps of the game are, then use the seven steps to decide how to implement each part. - Part 1: Complete the module
WGLib.py. While its functions are a notion of what they should be used for, exactly what they do, how they do it, and when/where you call them from another is up for you to decide. Test your functions early and often! - Part 2: Implement your game using your completed
WGLib.pymodule. - Part 3: Create two example runs using the provided
RecordMyRuns.pymodule. - Part 4: Complete your
RULES.md. - Part 5: Submit the assignment and complete the reflect form.
Part 0: Planning
This is a "soft" step in that it does not have items to assess; you will return to your RULES.md in Part 4 to polish the game instructions and other helpful info. This step is included to motivate being intentional with what separate functionalities you might require to handle each part of your game. Some questions to consider:
- What is the goal of the game? This assignment requires a representative "score" to be returned, reflecting the player's performance in some way. How might you use the accumulator pattern to update the score (and any other game state variables) on each player's input?
- What information is needed to fully describe the state of the game? For example, this might be a list of English words of a specific length, a list of all previous guesses made by the player, a number of turns remaining, a representation of correctly-guessed letters from a secret word, and so on.
- What actions can the player take? Since the interface is console-based, how will the player enter their action as a string? What information is needed to determine whether the player's input is valid?
- How should the game state be updated in response to (valid) player input?
- How should the game state be presented before each player input? (So they can follow along easily without keeping it in their head.)
- When is the game over? What information should be presented before terminating?
As you answer these questions, we recommend that you write yourself notes in your RULES.md under the bottom TODOs section. Update them as you make progress! As indicated in that file, this section will NOT be included in the published CS 101 Game Gallery page for your game after the assignment is over. It is entirely for your benefit as you work through the assignment!
Inspiration
The r/wordgames subreddit and related subreddits like r/Wordle have many discussions about various word games implemented elsewhere on the web (e.g., here, here, and here). Most of these games have polished graphical interfaces, but don't let that stop you! A console-based interface like the one we use for this assignment can typically be made to work reasonably easily, especially if there is not a substantial 2D spatial aspect.
Demo Game
See our Demo Game for a concrete example game within the scope of this assignment! It is intended to be a minimal example—think about how you might improve the interface for your own game!
Part 1: Implementing your Library/Helper Functions (WGLib.py)
The following functions are defined in WGLib.py and are expected to be called from within MyWordGame.playGame() in Part 2. (Click any function to jump to its full section below.)
parseWordsFile()promptPlayAgain()selectWords()validate()getValidInput()processValidInput()createDisplayString()
Note that these are not listed necessarily in the order in which you will implement or call them from the functions in MyWordGame.py in Part 2. Rather, they are roughly ordered in increasing order of your flexibility to implement them as desired for your game.
parseWordsFile()
Functional Requirements
- Given the path to a file with one word per line, return a list of all words in the order in which they were read, omitting any trailing whitespace or newline characters.
This function has no documentation requirements. A complete docstring has been provided for you.
promptPlayAgain()
Functional Requirements
- Prompts the player to enter either
yorn. - When the player enters an invalid string, prints a messaging indicating this to the player, and then the player is prompted again.
- Prints a message before each player input (either with
print()orinput()) and prints a message that informs the player of invalid input when they provide something other thanyorn.
This function has no documentation requirements. A complete docstring has been provided for you.
selectWords()
Functional Requirements
Given a list of strings
words, a non-negative integernum, and any additional parameters of your choice: return a list ofwordsthat satisfy any additional conditions of your choice so that:- If either
num == 0ornumis greater than the number of satisfying strings, return all of them. - Otherwise,
num > 0and is smaller than the number of satisfying strings. In this case, return anynumof them.
In other words,
selectWords()returns as many satisfying strings as possible up tonum; in the special case ofnum == 0, treatnumaslen(words).- If either
Documentation Requirements
- Replace the
TODOs in the given docstring with a proper description of your implementation of this function. - Add
:param:items for each parameter you introduce. - Add two doctests that call this function with different inputs and return different outputs. See our appendix on doctests and the top of the official
doctestdocumentation for an introduction to doctests, what they are, why we use them, and how to write them for this assignment.
Example implementations for known games
- Consider New York Times' Wordle. The player is guessing a secret word of a known length within a maximum number of turns. A turn is only complete once the player enters a word that (a) has the same length as the secret word and (b) is found in a reference list of English words. If
selectWords()had a parameter to specify a target length, then this method could be used to return all words of a given length. With such a list computed, it would be easy to determine whether a player's guessed word was valid by checking whether the guessed word wasinthe list. - Consider New York Times' Spelling Bee. The player is given a set of letters with the goal of making the longest English words using only those letters (multiple times allowed) possible. An additional point bonus is given for words that use all letters at least once. If
selectWords()had a parameter to specify a set of letters, then this method could be used to return all words spellable using only those letters. As in the previous example, it would be easy to determine whether a player's guessed word was valid by checking whether the guessed word wasinthis list. - Consider Seven Little Words. The player is guessing which strings can be concatenated together to make one of a list of unknown English words. If
selectWords()had a parameter to specify a set of letters, then this method could be used to return all words that can be partitioned into shorter strings of length 2 and 3.
validate()
Functional Requirements
- Rename the
*REPLACE_MEparameter and add as many as you need for your usage! - Returns
TrueorFalsedepending on whether the given stringxis valid according to properties defined by the other parameters (if any). - Returns
Truefor some inputs andFalsefor others.
Documentation Requirements
- Replace the
TODOs in the given docstring with a proper description of your implementation of this function. - Add
:param:items for each parameter you introduce. - Add two doctests that call this function with different inputs and return different outputs. See our appendix on doctests and the top of the official
doctestdocumentation for an introduction to doctests, what they are, why we use them, and how to write them for this assignment.
Example implementations for known games
- Consider New York Times' Wordle. The player is guessing a secret word of a known length within a maximum number of turns. The game is helpful to the player in that it does not allow guesses to be repeated, saving the player a wasted turn if they were to enter a guess a second time. By providing a collection of previous guesses to
validate(), the function can be used to check whether a guess has been guessed before. - Consider New York Times' Spelling Bee. The player is given a set of letters with the goal of making the longest English words using only those letters (multiple times allowed) possible. Unlike Wordle where repeating guesses has no benefit, scoring a long word many times could be advantageous. Hence the rules do not allow repeat guesses. Same as for Wordle above,
validate()could be used to check for repeat guesses if previous guesses are provided as an argument. - Consider Seven Little Words. The player is guessing which strings can be concatenated together to make one of a list of secret English words. Given a player's choice of strings to concatenate,
validate()can be used to check whether they make a secret word if the secret words (and possibly the pieces each one was split into) are provided as parameters.
getValidInput()
Functional Requirements
- Prompts the player for a string subject deemed valid according to the
validate()function. - If the player enters an invalid string (according to your
validate()function), a response including the word "invalid" is printed and then the player is reprompted for a valid string.
Documentation Requirements
- Rename the
*REPLACE_MEparameter and add as many as you need for your usage!
Tip
See the example implementations of validate() above for how this function, which must call validate(), could be used in different games.
processValidInput()
Functional Requirements
- Uses the information in the parameter(s) to update the game state. You may choose whether the state is mutated or not.
Documentation Requirements
- Rename the
*REPLACE_MEparameter and add as many as you need for your usage! - Replace the
TODOs in the given docstring with a proper description of your implementation of this function. - Add two doctests that call this function with different inputs and return different outputs. See our appendix on doctests and the top of the official
doctestdocumentation for an introduction to doctests, what they are, why we use them, and how to write them for this assignment.- If your function mutates the given parameters, then your test should compare the value of a passed argument after a function call to the correct expected value; see our example doctest to replace provided in the template file.
Example implementations for known games
- Consider New York Times' Wordle. The player is guessing a secret word of a known length within a maximum number of turns. The game is helpful to the player in that it does not allow guesses to be repeated, saving the player a wasted turn if they were to enter a guess a second time. By providing a collection of previous guesses to
validate(), the function can be used to check whether a guess has been guessed before. - Consider New York Times' Spelling Bee. The player is given a set of letters with the goal of making the longest English words using only those letters (multiple times allowed) possible. Unlike Wordle where repeating guesses has no benefit, scoring a long word many times could be advantageous. Hence the rules do not allow repeat guesses. Same as for Wordle above,
validate()could be used to check for repeat guesses if previous guesses are provided as an argument. - Consider Seven Little Words. The player is guessing which strings can be concatenated together to make one of a list of secret English words. Given a player's choice of strings to concatenate,
validate()can be used to check whether they make a secret word if the secret words (and possibly the pieces each one was split into) are provided as parameters.
createDisplayString()
Constructs and returns a string to display at the start of a turn using the information in the parameters.
Functional Requirements
- The returned string should describe the full state of your game so that players of your game understand what their current progress/status is. Use a multiline string and/or newline characters (
\n) to return multiple lines in one string.
Documentation Requirements
- Rename the
*REPLACE_MEparameter and add as many as you need for your usage! - Replace the
TODOs in the given docstring with a proper description of your implementation of this function. - Add two doctests that call this function with different inputs and return different outputs.
- If your function mutates the given parameters, then your test should compare the value of a passed argument after a function call to the correct expected value; see our example doctest to replace provided in the template file.
- See our appendix on doctests the top of the official
doctestdocumentation for an introduction to doctests, what they are, why we use them, and how to write them for this assignment.
Example implementations for known games
- Consider New York Times' Wordle. The player is guessing a secret word of a known length within a maximum number of turns. All previous guesses are displayed, color-coded so that each letter is (a) green if the letter appears exactly at this position in the secret word, (b) yellow if the letter is in a different position in the secret word, or (c) if the letter does not appear in the secret word at all. With some creativity, we can provide this feedback without relying on colored output (which is indeed possible in most terminals but not something we plan on implementing for the online game gallery), such as using punctuation symbols to mark a letter either next to each letter or below it (on the next line). It could be useful for the display string to include this info for all guesses, not only the most recent, plus how many turns/guesses remain.
- Consider New York Times' Spelling Bee. The player is given a set of letters with the goal of making the longest English words using only those letters (multiple times allowed) possible. It could be useful for the display string to include the letters available (always in the same order), the number of guesses remaining (the official game ends at the end of the day, not after any fixed number of turns), and the current score.
- Consider Seven Little Words. The player is guessing which strings can be concatenated together to make one of a list of secret English words. It could be useful for the display string to include which strings have not yet been correctly combined into a secret word, and separately, all of the guessed secret words. If there is a maximum number of turns, including how many remain is also helpful for the player.
Part 2: Implementing your Own Word Game (MyWordGame.py)
This part asks you to implement two functions, playGame() and startSession(). The former runs a single "play" of the game, whereas the latter runs an interactive session where the player will play the game, as many times as they like in a row, before quitting.
playGame()
Functional Requirements
- Prompts the player for at least one meaningful game setting/parameter before starting the actual game.
- While the player provides invalid answers, reprompt.
- For suggestions as to what you might prompt for here, see the Suggesions below and our Demo Game that prompts the player for two different game settings.
- Initialize the game state, meaning the variables needed to facilitate the game before it starts.
- For the game itself, uses the game loop pattern with a while-loop. During each game loop, it does each of the following at least once:
- calls
WGLib.createDisplayString()and prints its returned string, - calls
WGLib.getValidInput()to get validated player input, and - calls
WGLib.processValidInput()to process the player input and update the game state accordingly.
- calls
- When the game ends, a summary involving the player's final score is printed.
- Returns a non-negative
intthat represents the player's score.
Suggestions
- If your game has a win/lose condition as opposed to numeric scores, consider using
0for a loss and1(or any other positiveint) for a win. - To decide which game setting(s) to prompt the player for, consider the factors of your game that affect the difficulty.
This function has no documentation requirements. A complete docstring has been provided for you.
startSession()
Functional Requirements
- Calls
random.seed()at the start with the givenseedparameter. This has been done for you. - Begins a game without prompting by calling
playGame(). - Once the game ends, prompt the player whether they wish to play the game again by calling
WGLib.promptPlayAgain().- If so, start a new game with
playGame()(do NOT callrandom.seed()again). - Otherwise, print out a summary of the player's scores (e.g., highest score, average score, number of games, etc.).
- If so, start a new game with
- Returns the list of scores from all games played.
This function has no documentation requirements. A complete docstring has been provided for you.
Part 3: Recording Runs
By running the provided RecordMyRun.py module, the given main block will call your MyWordGame.startSession() function with a specific seed, keeping track of every string input by the player. Once the session ends, a JSON (JavaScript Object Notation) file is written that contains these inputs, along with the seed and all scores as returned by .startSession(). Such a run file can be "replayed" using the provided ReplayMyRun.py module by changing the appropriate line to read the run file (in JSON format) of interest. This allows you to test whether your gameplay is reproducible as it should be. (The autograder will be "playing" through your game by following these runs!)
Requirements
- Record a run named
one_play_only_valid_input.json(the.jsonextension is added for you by the module) which satisfies the following:- Plays only one game (
MyWordGame.playGame()andWGLib.promptPlayAgain()are called once each). - Only valid inputs are entered (whenever
WGLib.getValidInput()is called,WGLib.validate()is called once and returnsTrue). - The run ends normally (no crash/errors, returns a list with one game score).
- Plays only one game (
- Record a run named
multiple_plays_some_invalid_input.json(again, the.jsonextension will be added for you) that satisfies:- Plays at least two games (
WGLib.playGame()andWGLib.promptPlayAgain()are called at least twice each). - At least one invalid input is entered (
WGLib.validate()is called multiple times, returningFalseat least once before returningTrue, in some call toWGLib.getValidInput()). - The run ends normally (no crash/errors, returns a list with one score per game played).
- Plays at least two games (
Steps
To record the two required runs above:
- Open
RecordMyRun.pyin the directory with the rest of the assignment files in PyCharm. - (OPTIONAL) Change the seed from 101 to another integer.
- Run the module, which will call
.startSession()after starting to record all inputs during it. - Play one game, only making valid inputs. Once the game is over, say
nto not play a second game, ending the session.one_play_only_valid_inputs.jsonwill be created for you. - Next, play two or more games, making at least one invalid input while playing. Once
nis input to not play again, the session ends, andmultiple_plays_some_invalid_inputs.jsonwill be created for you.
TIP
If you make a mistake by giving unintended input, simply re-run the module. If you want to record only one of the two runs, comment out the appropriate line at the bottom of the file in the main block.
Part 4: Finishing your README
Finally, polish your RULES.md by replacing NAME_OF_YOUR_GAME in the main title then completing each of the following sections:
- Brief Description: A 1-3 sentence "hook" about your game.
- Rules of Play: Detailed instructions to play your game.
- Game Settings: The parameters chosen by the player before the game starts, such as difficulty settings.
See our example RULES.md at the bottom of the Demo Game page.
This file will be used to render the game page in the CS 101 Game Gallery. To get feedback, ask some peers to try out your game!
Part 5: Submitting and Completing the Reflect Form
Checklist Before Submitting
Along with your WGLib.py and MyWordGame.py modules, be sure to submit your two recorded run files and RULES.md.
Rubric
This assignment is out of 56 points.
Autograded portion [46 points]
- [29]
WGLib.py- [2]
parseWordsFile()Meets all requirements (functional): Correctly returns contents of given file. - [4]
promptPlayAgain()Meets all requirements (functional):- [1] Correctly returns either
TrueorFalseif first input is valid. - [1] Correctly reprompts when given invalid input.
- [1] Works as expected when called during run
one_play_valid_inputs.json. - [1] Works as expected when called during run
multiple_plays_invalid_inputs.json.
- [1] Correctly returns either
- [3]
getValidInput(): Meets all requirements (functional, documentation).- [1] Called and works as expected when called during run
one_play_valid_inputs.json. - [1] Called and works as expected when called during run
multiple_plays_invalid_inputs.json. - [0.5] Docstring has
TODOs removed. - [0.5]
REPLACE_MEparameter removed/replaced.
- [1] Called and works as expected when called during run
- [5]
selectWords()Meets all requirements (functional, documentation):- [1] Works as expected when called during run
one_play_valid_inputs.json. - [1] Works as expected when called during run
multiple_plays_invalid_inputs.json. - [0.5] Docstring has
TODOs removed. - [0.5]
REPLACE_MEparameter removed/replaced. - [2] Docstring contains doctests that call the function with different inputs and outputs and those doctests pass.
- [1] Works as expected when called during run
- [5]
validate(): Meets all requirements (functional, documentation). Same criteria as for the previous. - [5]
processValidInput(): Meets all requirements (functional, documentation). Same criteria as for the previous. - [5]
createDisplayString(): Meets all requirements (functional, documentation). Same criteria as for the previous.
- [2]
- [8]
MyWordGame.pyreplayed with runone_play_valid_inputs.json:- [4]
playGame(): Meets all requirements (functional):- Calls all required functions.
- Returns player's score as an int.
- Prints message including score at end of each game.
- [4]
startSession(): Meets all requirements (functional).- Calls
WGLib.promptPlayAgain()once after each game. - Returns the list of each score from the games played.
- Returned list of scores are those returned by
playGame()and match those in the recorded run file.
- Calls
- [4]
- [8]
MyWordGame.pyreplayed with runmultiple_plays_invalid_inputs.json: Same criteria as for the previous. - [1]
RULES.md- [0.5] Replaces
NAME_OF_YOUR_GAMEwith the name of your game. - [0.5] Includes filled-in sections:
Brief Description,Rules of Play, andGame Settings. (Contents will be graded manually!)
- [0.5] Replaces
Manually-graded portion [12 points]
- [12] Rest of submission
- [5] Accurate docstrings in
WGLib.pyforgetValidInput(),validate(),selectWords(),createDisplayString(),processValidInput(). - [5] Complete description, rules, and settings in
RULES.md - [2] Reflect form
- [5] Accurate docstrings in
Submission Instructions
- Log in to Gradescope.
- Under the CompSci 101 dashboard, click Assignment 5.
- Click Submit Programming Assignment, click "Click to browse", and select the required files. Click “Browse Files” again to select and submit more files.
- Once all files are selected, click "Upload".
You can submit as many times as desired without penalty. Your last submission will be graded.
Reflect Form
After submitting your assignment code, complete the reflect form found here.
Appendix: The doctest module
Doctests are example code snippets included directly in your functions' docstrings (a triple-quote multiline strings immediately following the function definition). They resemble the input and output from interactive sessions in the Python console, making them easy to read and understand by users of your code (yourself included).
OK, so they are useful as documentation. But what do they have to do with testing?
Their syntax, which is essentially Python with some additional prefixes on each line as seen in the Python console, allows one to execute the examples themselves. Each line begins with one of the following:
>>>, denoting an input line of Python,..., denoting an input line that continues the code block above it,- or no additional symbols, denoting either a output line (as if it were printed) or the returned value from an input line above.
Indeed, this closely resembles the Python console, and it typically suffices to simply copy/paste snippets directly from it into a docstring to be used as a doctest. By executing doctests, specifically with the doctest module from the Python standard library, the input lines are executed from top to bottom, as if in a Python console, and each output/result line in the doctest is checked against the real output/result line from the console. If all such lines are consistent, the doctest passes, and otherwise it fails. See the motivating example below, as well as our doctest included for WGLib.parseWordsFile() in the provided starter files.
An example
To illustrate how to write doctests, let's consider the simple function add() below:
python
def add(a, b):
"""
Add two numbers together.
:param a: first number
:param b: second number
:return: the sum of a and b
"""
return a + bTo gain confidence that our implementation is indeed correct, let's consider the following example calls:
add(-1,1)should return0,add(10,5)should return15,add(2.5,4.5)should return7.0, andadd(0,0)should return0.add(x,x)should return twice the value ofx, for anyintorfloatparameterx. For sake of exposition, let's suppose our parameter is ratherlen(lst)for some listlst, to illustrate the...syntax of doctests.
At this point, we could type these in as doctests, then call doctest.testmod() in the main block of our file with the add() function above. To make this concrete, let's first do something more familiar—using the Python console to verify these tests manually.
If we paste the definition of add() (without the comments to save space) into a new Python console, we can see that indeed the output is as we expect:
python
>>> def add(a, b):
... return a + b
...
>>> add(-1,1)
0
>>> add(10,5)
15
>>> add(2.5,4.5)
7.0
>>> add(0,0)
0
>>> lst = [
... 1,2,3,
... 4,5,6,
... 7,8,9,
... ]
>>> add(len(lst),len(lst))
18So we have manually tested and verified our function. To provide these examples as doctests, we can manually type them in using the syntax described above, or simply copy/paste the Python console above (without the function definition itself) then make its indentation match the rest of the comment. This yields the following:
python
def add(a, b):
"""
Add two numbers together.
:param a: first number
:param b: second number
:return: the sum of a and b
>>> add(-1,1)
0
>>> add(10,5)
15
>>> add(2.5,4.5)
7.0
>>> add(0,0)
0
>>> lst = [
... 1,2,3,
... 4,5,6,
... 7,8,9,
... ]
>>> add(len(lst),len(lst))
18
"""
return a + bAs mentioned above, the whitespace on the left of each added line is to match that of the rest of the comment. You can see more details about how flexible (or not) the indentation of doctests are in the module's documentation.
Finally, to execute these doctests, it suffices to simply import the doctest module in the same module as the function you wish to run the doctests of (typically in the main block), then call doctest.testmod(). This is precisely the two lines included at the bottom of WGLib.py, which allows you to easily run and check the validity of your own tests. (If you run the file as it is in the starter zip archive, most will fail, of course.)
Note, however, that passed tests do not have any output! Hence, for completion, we include the full code described here, but specifically we include the optional parameter verbose=True in the last call. The output is also included. By pasting the code into a file of your own, you can verify the process on your own machine.
Full add() example that runs all doctests
file AddDocTestExample.py:
python
def add(a, b):
"""
Add two numbers together.
:param a: first number
:param b: second number
:return: the sum of a and b
>>> add(-1,1)
0
>>> add(10,5)
15
>>> add(2.5,4.5)
7.0
>>> add(0,0)
0
>>> lst = [
... 1,2,3,
... 4,5,6,
... 7,8,9,
... ]
>>> add(len(lst),len(lst))
18
"""
return a + b
if __name__ == '__main__':
import doctest
doctest.testmod(verbose=True) # verbose enabled to see passed testsoutput:
Trying:
def add(a, b):
return a + b
Expecting nothing
ok
Trying:
add(-1,1)
Expecting:
0
ok
Trying:
add(10,5)
Expecting:
15
ok
Trying:
add(2.5,4.5)
Expecting:
7.0
ok
Trying:
add(0,0)
Expecting:
0
ok
Trying:
lst = [
1,2,3,
4,5,6,
7,8,9,
]
Expecting nothing
ok
Trying:
add(len(lst),len(lst))
Expecting:
18
ok
1 items had no tests:
__main__
1 items passed all tests:
7 tests in __main__.add
7 tests in 2 items.
7 passed and 0 failed.
Test passed.Appendix: (OPTIONAL) Generating Hints with DukeGPTHints.py
This module is provided so that you can generate brief crossword-style hints for words in your game. For example, Seven Little Words provides the player with hints for each of the secret words so that the player has more information when guessing (ideally without giving them away!).
In order to use this function, you must obtain your own API key from Duke's AI Dashboard in order to successfully connect. This is a quick process, only a few minutes:
- Log into the AI Dashboard at https://dashboard.ai.duke.edu/.
- Click the "AI Gateway" tab
- At the top, click the "Create Api Key" button
- Copy/paste the API key (begins with
sk-) into the providedduke_ai_api_key.txt, overwriting the invalid placeholder key.
After these steps, you should be able to run the DukeGPTHints.py module, which will read your provided API key and use it to access Duke's LLM endpoint. For use in your game, import this module at the top of MyWordGame.py and/or WGLib.py as needed to call DukeGPTHints.generate_hint(). See the docstring in that module for details of how to call it.
WARNING
For our autograder to reproduce game behavior based on your provided runs (see Part 4), the hints must not affect the game flow/logic. Treat this restriction as being required to only print the hints and do not involve hints when deciding whether a player input is valid (e.g., in getValidInput() or validate()). If the hints affect game logic, then it is possible that the autograder will fail for only this reason to replay your runs!