Title image of Recreating Wordle in Blazor

Recreating Wordle in Blazor

26 February 2022

·
Blazor

In my last post, we wrote an algorithm to solve Wordle. This was fun and all but a console app is pretty boring to look at. I feel like doing something more visual. So along with the same Wordle theme, I’ve decided to try and recreate the game in Blazor.

Blazor is something that’s interested me for a while. Being able to utilize my C# knowledge to build client-side apps is super cool. One of the aims of this project is to find out how much Javascript will still be needed to get Wordle working in Blazor.

I’m only going to cover the most important points here, the full code will be on Github.

The board

The Wordle board is a 6 x 5 set of squares. To represent this in C# I used an array of char arrays. Using arrays instead of lists allows other code to pick out specific values which makes things a bit easier.

Diagram showing how arrays are used to represent the Wordle board

Diagram showing how arrays are used to represent the Wordle board

Razor loops go through to render each square. Each square is positioned using Bootstrap flexbox and styled with custom classes:

@for (int guessNumber = 0; guessNumber < guesses.Length; guessNumber++)
{
	<div class="d-flex flex-row">
		@for (int i = 0; i < guesses[guessNumber].Length; i++)
		{
			<div class="m-1 text-center d-flex align-items-center game-piece @GetGamePieceClass(guesses[guessNumber][i], i, guessNumber)">
				<p class="w-100 m-0">@(guesses[guessNumber][i] != '\0' ? guesses[guessNumber][i] : "")</p>
			</div>
		}
	</div>
}

Handling input

This was the trickiest part of the project. When playing Wordle you don’t need to select the box to enter each letter. A player can type anywhere on the page and the letters get added to your guess. This makes the game very slick to play and definitely something we need to replicate.

Blazor can add C# event handlers to any element on the page but not the page as a whole. As far as I can tell this is something only Javascript can do. Blazor has good Javascript interoperability so it’s not impossible but it’s not as simple as adding an event handler to a standard input element.

First, to set up the event handler we need to call a Javascript function from C#. Then to handle each event we need to go the other way round from Javascript to C#.

Diagram showing the setup of input event handling

Diagram showing the setup of input event handling

In the Blazor OnInitialized method we invoke the Javascript needed to attach the event handling to the HTML Document. As part of this, we pass in a DotNetObjectReference used by the JS to call back to C# when an event happens:

@inject IJSRuntime JS

// .....

@code {
	private DotNetObjectReference<Index>? objRef;

	protected override async Task OnInitializedAsync()
	{
		if (!EventHandlersSet)
		{
			objRef = DotNetObjectReference.Create(this);
			await JS.InvokeAsync<string>("wireUpEventHandler", objRef);

			EventHandlersSet = true;
		}
	}
}

The JS shows the event listener is attached to the document for key-up events. The dotNetHelper is used to call C# methods when a key is pressed:

window.wireUpEventHandler = (dotNetHelper) => {
    document.addEventListener('keyup', (event) => {
        var name = event.key;
        var code = event.code;

        dotNetHelper.invokeMethodAsync('HandleKeyPress', code);
    }, false);
};

Finally, the C# method HandleKeyPress activates game logic based on the key pressed:

[JSInvokable]
public async Task HandleKeyPress(string value)
{
	if (gameState != GameState.Playing)
	{
		return;
	}

	if (value == "Enter")
	{
		// ........
	}
}

Toasts

Wordle provides players with feedback through Toast notifications at the top of the page. I think it’s possible to do a toast exclusively in C# and Html (Which I think would be a fun project). But for simplicity, I’ve chosen to just use a Javascript library to do it.

Toastr.js is a nifty little library with some nice customization options. Wordle shows two types of messages during a game. A warning message for invalid guesses and a success message for completing the Wordle. Here’s Javascript for the toasts which we can call from C#:

window.toast = (category, message) => {
    toastr.options.positionClass = "toast-top-center";

    if (category === 'success') {
        toastr.success(message);
    }
    else {
        toastr.warning(message);
    }
};

Loading words file

To check a word exists we need a dictionary of words to compare it against. If the word does not exist in the dictionary it’s not a valid word and we don’t accept the guess. From our Wordle algorithm, we already have a decent list of words we can use.

We could hard code the words into the C# code which might be better. But I’ve just loaded them straight from the txt file. To do this you need to place the text file in the wwwroot folder of your Blazor project. Then use the HTTP client to read it like this:

using (StreamReader reader = new StreamReader(await Http.GetStreamAsync("words/5-letter-words.txt")))
{
	var newWords = new List<string>();

	while (!reader.EndOfStream)
	{
		newWords.Add((await reader.ReadLineAsync()).ToUpper());
	}

	words = newWords;
}

As well as checking guesses are valid we can use the words for the initial Wordle word. Here we pick one randomly when the game is reset:

var rnd = new Random();
Wordle = words.ToArray()[rnd.Next(words.Count() - 1)].ToUpper();

Dynamic Classes

The letter square has four different states: empty, active, yellow and green. Each state has different styling which we’re going to apply using CSS classes. We can use C# to dynamically apply the right class depending on the letter in the square.

The logic for the different classes is stored in the GetGamePieceClass method:

private string GetGamePieceClass(char letter, int position, int guessNumber)
{
	// Player hasn't entered this guess yet - show border around letter
	if (guessNumber == CurrentGuess && gameState == GameState.Playing)
	{
		if (letter != '\0')
		{
			return "active";
		}
	}

	// Correct letter in correct position - show green background
	if (Wordle[position] == letter)
	{
		return "success";
	}

	// Correct letter - show yellow background
	if (Wordle.Contains(letter))
	{
		return "partial";
	}

	// Incorrect letter - show grey background
	if (letter != '\0')
	{
		return "fail";
	}

	// Empty square - show white background
	return string.Empty;
}

GetGamePieceClass is called in the class section of each of the div squares:

<div class="m-1 text-center d-flex align-items-center game-piece @GetGamePieceClass(guesses[guessNumber][i], i, guessNumber)">
	<p class="w-100 m-0">@(guesses[guessNumber][i] != '\0' ? guesses[guessNumber][i] : "")</p>
</div>

Final thoughts

Apart from some trickiness around setting up the input handling Blazor has made recreating Wordle very easy. It’s really nice to be able to write the logic of the game in the language I’m most familiar with. And it’s been easy to call JS methods where necessary like the Toast notifications. But I could see the JS interoperability becoming a little messy in larger applications.

One thing missing from my recreation is Wordle’s animations. When a player gets a guess wrong the tiles are supposed to shake. Feel free to try to add this to the app, all the code if of course on Github: https://github.com/Liam-Hunt/blazor-wordle