Wordle is a great challenge for the Power Apps creator (in fact, any developer) as it is a very simple game that can be rebuilt in a matter of hours. As such, it’s a fun mini-project that demonstrates the benefits of approaching a project in a structured way and establishing clean logic.
Initialisation
On the OnVisible property of our screen, we add the following:
Set(numRows, 1);
Set(varAnswer, "PARTS")
We can randomise the 5-letter word at a later point in development. numRows tells us how many attempts have been made.
Canvas elements
First we need to set up our UI. The game grid will be built using a vertical gallery with a horizontal gallery nested inside. Give the vertical gallery the following properties:
- Items: Sequence(numRows)
- Whenever the user makes an attempt, we will increment numRows which will add a new line to the gallery
- Template size: 80
- Width: Self.TemplateHeight * 5
- This gives us equally-sized squares in each gallery cell
- Height: Self.AllItemsCount * Self.TemplateHeight
Inside the vertical gallery we will add a label and a horizontal gallery. The label will hold the value of the current row – this will be required later. Set the horizontal gallery up as follows:
- Items: Sequence(5)
- Template size: Self.Width / 5
- Width: Parent.Width
- Height: Parent.TemplateHeight
Inside the horizontal gallery, insert a text input. Set its height and width to the template dimensions of the gallery. Max length must be set to 1 for obvious reasons. Set it’s default value to:
If(
Value(Label1.Text) = numRows,
"",
LookUp(
colInputs,
Row = Value(Label1.Text) &&
Col = ThisItem.Value
).Value
)
colInputs will give an error right now – ignore that for now. Label1 is the label that we added to the vertical gallery. This label can be referenced to get the current row number from within the horizontal gallery. This is a neat little hack, as otherwise we can’t reference this value. The code above loads in a value from a collection that holds the user’s inputs, using a position reference which is a combination of row and column to locate it.
On the OnChange event of the text input, add this:
If(
IsBlank(
LookUp(
colInputs,
Row = Value(Label1.Text) &&
Col = ThisItem.Value
)
),
Collect(
colInputs,
{
Row:Value(Label1.Text),
Col:ThisItem.Value,
Value:Self.Text,
Check:""
}
),
UpdateIf(
colInputs,
Row = Value(Label1.Text) &&
Col = ThisItem.Value,
{
Row:Value(Label1.Text),
Col:ThisItem.Value,
Value:Self.Text
}
)
)
This checks to see if there is a value written for this position. If there is, it is updated with the typed value. Otherwise, the inputs collection is populated with a new record for the input.
Note that we add a blank ‘Check’ column here. This is going to hold a result that is calculated when the attempt is submitted. As players of the game will know, letters can be scored as white, amber or green:
- White – letter is not in the target word
- Amber – letter is in the target word, just not that position
- Green – letter is in the target word in that position
Add a submit button to the canvas. When clicked, it will check the current row and calculate the results. Here is the code:
ForAll(
Filter(
colInputs,
Row = numRows
) As Inputs,
Switch(
true,
// match
Inputs.Value = Index(Split(varAnswer, ""), Inputs.Col).Value,
UpdateIf(
colInputs,
Col = Inputs.Col &&
Row = Inputs.Row,
{
Check:"Correct"
}
),
// partial match
Inputs.Value in Split(varAnswer, "").Value,
UpdateIf(
colInputs,
Col = Inputs.Col &&
Row = Inputs.Row,
{
Check:"Place"
}
)
)
);
// check for game over
If(
numRows = 5 || CountRows(Filter(colInputs, Row = numRows && Check = "Correct")) = 5,
// game over
UpdateContext({varComplete:true}),
// add new row
Set(numRows, numRows + 1)
);
First of all, we get the inputs for the current row to be iterated. We then use a Switch() statement to test for the different result types listed above. We list in order of difficulty to achieve, taking advantage of the fact that Switch() will terminate at the first successful condition. If the letter is exactly right, the other conditions are not tested, which is exactly what we want.
To check for an exact match, we use Split() to break the target word into a table of 5 records (letters) and use Index() to find the letter for the position of the current iteration. If these match, we have a green result!
Amber is easier to calculate, as we just need to check for the presence of the input anywhere in the target word. We do not specify a fallback action for the Switch() statement, as letters not in the target are simply left as is.
The final piece of code checks to see if the user has made all possible attempts, or if the last attempt had 5 successful green results, and shows a completion message if they have. Otherwise the row counter is incremented and the user makes another guess.
To tidy up, we need to disable our submit button if we don’t have 5 letters entered for the current row, which we can do as follows:
If(
CountRows(
Filter(
colInputs,
Row = numRows
)
) < 5,
DisplayMode.Disabled,
DisplayMode.Edit
)
We also need to disable text inputs on completed rows:
If(Value(Label1.Text) = numRows, DisplayMode.Edit, DisplayMode.View)
And we apply a fill colour to our text inputs using this code:
Switch(
LookUp(colInputs, Col = ThisItem.Value && Row = Value(Label1.Text)).Check,
"", Color.White,
"Correct", Color.SeaGreen,
"Place", Color.GoldenRod,
Color.White
)
And there we have it! Wordle replicated in Power Apps in under an hour! Now we can work on randomising the target word, but for that we need a dictionary of possible words, and a mechanism for ensuring the same word doesn’t crop up twice – I’ll leave that up to you!
Leave a Reply