Wordle is a popular word-guessing game hosted by NYTimes. The player attempts to find a 5-letter target word in 6 guesses or less. For each guess, the game shows the letter in green if it is in the correct position and yellow if it appears in the word but at a different location. If a letter appears more than once in a guess word, the number of times that letter appears as yellow or green depends on how often the letter appears in the target word. A new target word is picked every day, so players can compare their results on a daily basis. We highly recommend playing the game once to get a basic understanding.
A common strategy is to pick words that contain very frequent letters. For
example, AISLE
is good because it contains three most common
vowels and two other common letters. But at some point the player has to
switch to finding words that match the given clues. This is a large part of
the game, and "AI" solutions do not have to be very intelligent because
they can simply run through the list
of possible
answers, testing which work. If you could memorize the full word list,
you too would play as well as a computer! The fun of the game is trying to
recall words that fit.
As a player, I have a question: how often are my guesses really good ones? That is, how successful am I at finding intermediate words that narrow the choices quickly? A tool to evaluate guesses could also be useful to beginners. You are to write a program that computes the number of words that match a given word and displays those alternatives.
The program will read a list of 5-letter words, all in lower case. This
list will be terminated by the word END
(in upper case). This
will be followed by up to 10 guesses (just to allow the player
opportunities to explore further). For each guess there are two items: the
first is the word (5 lower case letters) and the second is 5 characters
indicating which of the guess letters match. In this second item, a period
('.') indicates a letter which matches the position, a dash ('-') indicates
a complete miss, and a question mark ('?')
indicates a letter
that appears in the word but not at the given position. You
will not implement the rule that if a letter appears more than
once in a guess but only once in the target, only one of the two
occurrences in the guess can be marked with '.' or '?'
. The
output will be a count of how many words can match and the list of the
words that match. List the words in the same order they appear in the
input.
This is an individual assignment. If you are having difficulties, it would be very good to talk to your instructor because there are likely logic issues that you need to discuss. Maybe we need to add more hints!
For example, if the input is
anvil chard chase cross phase proud spade spate train END aisle ?-?-. choir -----the the output will be
Possible guesses after aisle, choir: 2 spade spate
main
), and solutions which
do not involve at least 4 functions (other
than main
and run_checks
) will
be worth very few points. The 15-line limit includes blank
lines; see the class coding
standard.
string
mywords[MAX];
) in your solution. Using vector
and
other Standard Template Library (STL) classes will result in large
deductions. The only STL class you can use is string
(along with the string methods).
sizeof
in your solution. The
way that students want to use it - find the size of an array - works
reliably in very specific contexts. Use a separate variable to track the
size of your array and pass that information as a parameter to where it
is needed.
cout
and <<
. That is, do not use std::format
or printf
in your solution.
bool can_match(string possible_answer, string guess, string letter_matches);where
possible_answer
is a word from the dictionary of possible
answers that appears at the beginning of the input, guess
is
the word entered by a user, and letter_matches
is the string
of '.'
, '-'
, and '?'
characters
capturing which letters in the guess are "correct" (as described
above). You must use this function as the core of your solution.
main
must be to call the
function run_checks
. The body of run_checks
is
to include
assert( can_match("abcde", "axbxf", ".-\?--")); assert(!can_match("abcde", "axbxd", ".-\?--")); assert( can_match("abcde", "easty", "\?\?---")); assert(!can_match("abcde", "abcdf", ".....")); assert( can_match("abcde", "uvwxy", "-----"));as the first five lines. This will give some assurance that your implementation of
can_match
works. You can add additional
tests if you like. The assert
function executes an
expression and reports an error (and halts) if the expression is
false. This gives a simple way to implement unit tests. Bigger projects
would use CLion's unit test feature, but calling assert
requires little configuration. All you need to do is
#include <cassert>at the top of your file. Note the backslashes are necessary since
??
can have a special meaning in a string.
main
. Use prototypes for the functions called
by main
and put their definitions later in the file
(in any order).
The first test will have an empty set of words and empty set of guesses. You need just print
Possible guesses after : 0and exit. This means stage 1 is correct simply by writing
can_match
and ensuring it passes the above assertions.
You can receive 40% of the points for this assignment by completing just
this portion. Note there is a space between the
word after
and the colon - that is because there are no
guesses and having a space there allows writing a simple loop printing all
of the guesses.
Complete the rest of the lab. You will probably want to start by simply
reading the input words and guesses, then writing a function to fill an
array possible_answers
(or some similar name) with all words
and prints out that list, then refine the function to compute the possible
answers by first checking each word is allowed by the first guess, and
finally filter out all words that are not allowed by any of the
guesses. But there are different implementation strategies; use whatever
works for you.
This section gives further hints for portions of this assignment. You do not have to follow these hints, but they might save you some time.
END
, the second will read words and match
information until you reach end-of-file. You do not have to check that
either the number of input words or the number of guesses is less than
the limits published above.
x
is a string variable, cin >> x;
skips any leading whitespace and then reads characters until the next
whitespace (a "word"). Some of the input files have multiple words on
each line; this will not be a problem as long as you do not try to read
whole lines at a time rather than words. The same applies for the guesses
after "END". Trying to read a line at a time will go down a rabbit hole
of trouble on this assignment.
.find
method. This method returns the
constant string::npos
if the item is not found,
something else if it is. For example,
if ( s.find('R') == string::npos ) cout << "R not in string " << s << endl;However, you can simply write a function if you prefer, say
contains
, that uses a loop to check if a character
is in a string.
can_match
, look
back at the top of this writeup. If a guess is coder
with
the match code .-?--
, then the period means a word must
start with 'c', the - means it cannot have an 'o' in it (anywhere), the
question mark means it must have a 'd' but the 'd' cannot be in the middle
(position 3), and the final dashes mean the 'e' and 'r' cannot appear in the
word.
list
or vector
in your solution. A key
part of this assignment is learning to process simple arrays.
If your project is open in CLion, click on the three vertical dots next to the debug symbol, select Edit... (for the run configuration), select the Redirect input from box, and browse to a file like 2.in. Then you can run and debug your code as usual with the input coming from this file instead of the console (that is, instead of the keyboard).
string a = "one",
b;
then this error might be thrown for a[4]
or b[0]
.
.size()
result. The
size is unsigned, and this can mean that comparisons are not always
valid. The fix is to use size_t
:
for(size_t ix = 0; ix < s.size(); ++ix) ...This gives an unsigned integer of the right size for the possible length of a string. The same applies for the results from
.find
:
size_t search_result = s.find('x'); if ( search_result == string::npos ) ...