Wordle 3: analysing the effectiveness of the solver
1

First, set the browser to Full Screen view, to give this worksheet and your code as much space as possible.

Then choose either one of the two options below:

Option 1: Start this worksheet from scratch

- First Enter your name:
- Then
  .
- All answers that you subsequently enter/edit will be auto-saved to this sheet.

Option 2: Continue, or view, previous work

- All answers that you subsequently enter/edit will be auto-saved to this sheet.
- The first time you re-load an answers sheet - within a single session on Elan - you will be asked to confirm that you understand this.

This worksheet is copyright © Richard Pawson 2025, and protected by the Creative Commons license: Attribution-NonCommercial-NoDerivatives 4.0 International. You may freely make and distribute copies of this worksheet as is, but if you modify this worksheet you may not distribute your modified version (outside your own teaching institution) without the author's permission. Please email the author to report errors or suggest improvements.

# Step 1: introduction

Following-on from 'Wordle 2: writing an automated solver', in this worksheet your challenge is to write an analyser that can evaluate:

1. Just how effective that automated solver is at solving puzzles, by getting it attempt to solve all 2,315 of the valid target words, and measuring the percentage that it solves within six attempts. For those successful cases, it should also calculate the average number of attempts required.
2. Whether ARISE is the best starting word for that algorithm or, if not, what is the best starting word.

and then immediately file > auto save the code to your local file storage, because you will be making changes and want to ensure that your changes are saved.

Notice the following:

- Underneath all the `import`s you will see all the code you have written in the previous projects, plus some new code that you will be using and/or modifying in this worksheet.
- The new code currently has no `main` routine - but you will be adding one.
- There are two ghosted tests at the bottom, each covering several test cases and referring to functions that currently have only 'stub' implementations.

#### Notes

Step Completed Total hints used: /

# Step 2: implement 'attemptsNeededToSolve'

- Unghost the test named attemptsNeededToSolve. This will cause the tests to run, but they will all fail because the function attemptsNeededToSolve immediately above the test is just a 'stub' - the minimum code needed to compile.
- A proper implementation for attemptsNeededToSolve will re-use quite a bit of the logic from playReverseGame but with these differences:
  - it is a function: it will not result in anything being displayed, so there is no need for the `call` putAttemptIntoGrid, nor to create `grid` in the first place.
  - it does not need to, and indeed cannot, ask the user for input - so it should not have the `call` enterMark instruction. Instead, we can get the `mark` by evaluating the markAttempt function that we wrote in Wordle 1. However, we don't then need to use two instructions to obtain the mark - we can write it as a single `let` instruction. (It could still be a `variable`, but we have no need to re-assign it because it defines `mark` anew each time around the `while` loop, anyway).
  - The `while` loop need test only for whether the puzzle is `solved`, not for the number of attempts, because we want to know how many attempts it takes even if that is 7 or more - though any such case will eventually be counted as a failure to solve the puzzle.
  - Because we will not be updating the display grid (where rows and columns start from zero), `attemptNo` should be initialised to `1` rather than `0`, and it should be incremented by `1` whenever the `mark` for an attempt is not all-greens.
- Apart from those constraints, the rest of the logic required for attemptsNeededToSolve exists in playReverseGame and you will be able to copy several whole instructions from one to the other. Have a go at this now - using available hints only if you need them.

Hint 2-1: implementation in outline form
Hint 2-2: complete implementation

When your implementation of attemptsNeededToSolve is complete, all the test-cases (`assert`s) in `test` attemptsNeededToSolve should pass.

#### Notes

Step Completed Total hints used: /

# Step 3: write a main to use the new function

Write a `main` routine that:

- asks the user to enter a target word,
- evaluates the attemptsNeededToSolve function for this target using ARISE as the first attempt,
- prints the result in the form (example only): 'Target word QUITE took 4 attempts to solve'

Hint 3-1: outline instructions
Hint 3-2: the input and output
Hint 3-3: complete main routine

When you have this implemented, run the program to find out how many attempts it takes to identify the target words CHURN and then FIZZY.

Record your results here:

#### Notes

Step Completed Total hints used: /

# Step 4: analyse the algorithm for all potential target words

In order to identify the effectiveness of a Wordle solver we need to test it against all 2,315 valid target words, and from this determine the percentage of of the puzzles it was able to solve in 6 or fewer attempts, and, for those successes, the average number of attempts needed.

- Find the ghosted test named analyseAlgorithm and unghost it - which will cause the test to run and the `assert`s to fail. Then look at the stub implementation of the analyseAlgorithm just above the test.
- Note that the return type for this function is `(Float, Float)` which means that it returns a '2-tuple'- a tuple containing two values - which both happen to be Float in this case. A tuple is just a very simple, small, data structure for passing around more than one piece of data - of the same or different types - in one go.
- As it stands, the function always returns a pair of zeros. Take note of the syntax for constructing the 2-tuple to be returned: `tuple(something, something)`.
- In the analyseAlgorithm function, define two variables:
  - `success`, representing the number of puzzles solved within 6 attempts. For now, initialise it to an example value of `7`.
  - `weightedSum`, representing the sum of: (puzzles solved in one attempt x 1) + (puzzles solved in 2 attempts x 2) + and so on. For now, initialise it to an example value of `8`.
  (The choice of those two temporary initial values will become apparent shortly). These are both variables because later on we will need to replace the initial value with a calculated one in each case.

- Below this, define two more values - `successPercent` and `average` - but for each these use a `let` instruction (because we won't need to re-assign them):

  For `successPercent` give it a calculated value, derived from `success` such that it will represent a percentage of the number of `validTargets` rounded to one decimal place..

  The `average` is the `weightedSum` divided by `success` rounded to two decimal places.

- Change the `return` instruction to return a tuple constructed from the values `successPercent` and `average` in that order.
- If you have coded this correctly, then the first `assert` in the test should pass - but this is an artifical result, down entirely to those numbers used to initialise the variables! So once you have established that your implementation so far is correct, change the initial values of `success` and `weightedSum` to zero (causing the `assert` to fail again.)
Hint 4-1: rounding
Hint 4-2: the two expressions in full
Hint 4-3: the complete function so far

#### Notes

Step Completed Total hints used: /

# Step 5: Calculate the number of successful solutions

Now we will calculate `success` the total number of puzzles that the algorithm solves within 6 attempts.

Add a loop that evaluates the number of attempts to solve each of the targetwords provided in `validTargets`, and if that number is within the limit of six, increment the value of `success` by one.

Hint 5-1: the new code in outline
Hint 5-2: the complete function so far

Although none of the asserts in the new test will be passing yet, what do you notice about the two values that are being generated by the function in the test?

#### Notes

Step Completed Total hints used: /

# Step 6: Calculate the weighted-sum of the number of attempts

Now we will calculate the `weightedSum` of the number of successful attempts, from which the `average` is subsequently derived.

- Just before the loop, define a variable `outcomes` as an array of integers with seven members, initialised to zero. (Strictly speaking we could manage with six members, since we are interested only in the outcomes of 1 to 6 attempts - so the first element (number `0`>) is wasted. However, that's a small price to pay for simpler code - as you will see.
- Within the loop, whenever a target has been identified within six attempts, increment by one the value in the array corresponding to the number of attempts taken.
- Immediately after the end of the current loop, add a new `for` loop that goes through the index values from 1 to 6 (we can just ignore the unused element zero), then adds into `weightedSum` the value of that element times the index number. Make sure you understand why this will produce the 'weighted sum' of the results.

Hint 6-1: the array definition
Hint 6-2: how to update the array
Hint 6-3: the new loop in outline
Hint 6-4: the completed instruction for updating 'outcomes'
Hint 6-5: the completed function

When implemented, all the `asserts` in the `test` analyseAlgorithm should be passing.

#### Notes

Step Completed Total hints used: /

# Step 7: Re-write main to present the results of the analysis

Your task is now to re-write `main` to understake the analysis and present the results.

- Start by deleting all the existing instructions within `main`.
- Because we don't know how long this analysis may take, at the beginning of `main` print the message 'Starting...' onto the display.
- The `constant` `allValidTargetWords` is a single string containing 2,315 words separated by spaces. However, the function analyseAlgorithm expects a list of words, where each member of the list is a single word, with no spaces. If you are not familiar with how to do this, look up the various dotted-methods available on a String show in Help.
- Evaluate the function analyseAlgorithm, passing in the `"ARISE"` as the chosen first attempt, and the list of inputStringWithLimits you have just created from `allValidTargetWords`. Because this will pass back two values as a tuple, look up how to 'deconstruct' a tuple (show in Help) into two values named `successPercent` and `average`. (You can do this in a `let` instruction.)
- Print these values in the format: 'xx.x% of the puzzles solved within 6 attempts, with an average of x.xx attempts'

Hint 7-1: expression to split the target words into a list
Hint 7-2: deconstructing the tuple returned by analyseAlgorithm
Hint 7-3: how to create the formatted output
Hint 7-4: the completed 'main'

Run the program

Record the results (% solved and the average number of attempts) here:

Finally, try changing ARISE to an alternative first attempt word. Maybe try a few likely candidates.

Record the result from the best first attempt word that you found, whether or not it was better than ARISE.

#### Notes

Step Completed Total hints used: /

# Congratulations! Worksheet completed

Hopefully you were impressed at the effectiveness of the algorithm that you wrote. It would likely beat many human players - over a reasonable run of puzzles!

You now have the option to

to explore the parts of the code that you haven't written such as the display logic.

If you would like to share the working application with others, click file > save as standalone to save it as a self-contained single web page that shows only an enlarged version of the display tab runs the program automatically when it is opened, and which runs automatically when opened. This standalone email file may be emailed to others. You might like to print a few more instructions within `main` and/or add any of the refinements below.

If you have time, there are plenty of ways that you could extend this investigation into Wordle, including:

- Add a loop around the last three instructions in `main` to evaluate the effectiveness of using each of the valid target words as the first attempt, and keeping a running track of the best so far. Warning: this could take several hours to complete! Even more ambitious (and taking more than seven times longer) would be to try it for each of the 15,000+ validAttempt words as the first attempt.
- Read up online about more sophisticated algorithms - several of which are guaranteed to solve 100% of puzzles in five or fewer attempts, and with an average of 3.5 or lower. And someone has calculated the optimum strategy but to replicate that work, you're gonna need a bigger computer!
- Since the source code includes the code written in the previous worksheets - Wordle 1 and Wordle 2 - you might like to go back to the end of your saved versions of those two worksheets and implement any of the options suggested there, or combine them in some way.