Create your Wordle like game with Blazor and deploy to GitHub Pages
Welcome to my Everything in C# series. Wordle is a recently famous word guessing game like Mastermind where each guess return whether each letter is in the right position or just contained in the answer. This time we are going to implement a similar version with Microsoft Blazor technology. I have put my game on GitHub pages. You can take a look and play if interested.
What is Blazor?
Blazor is a technology that can run C# code on browser using WebAssembly. In past .NET developers relies on Razor pages for front-end development. Now we can use Blazor to create SPA as if we are using other framework like React/Vue/Angular.
For more information you can check this link: https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor
Getting Started
If you do not have .NET SDK installed please follow the steps here: https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/install
We are creating a pure client side game with Blazor WASM mode. Once finished you can deploy your game to static web hosting service (e.g. GitHub Pages) and let people play around.
- Create your project directory.
2. Open your command prompt / Terminal and change to your project directory. Execute dotnet new blazorwasm
. Your project folder should look like this.
3. Execute dotnet run
to check if it works.
When everything works fine. Let’s start to build our game.
Game Logic in C#
1. We need a way to load the answers, thus we have the following class:
2. Then we need to add this into service container. Open Program.cs
, look for lines builder.Services.xxxx
and insert the following code:
builder.Services.AddSingleton(
sp => {
var httpClient = sp.GetRequiredService<HttpClient>();
const string filePath = "word-list.txt";
return new AnswerProvider(httpClient, filePath);
});
Your Program.cs
file should look like this:
The code here tells Blazor to return the same object (Singleton) when requested by Pages/Components and config the AnswerProvider
that word-list.txt
is the source of answers file.
3. Now it comes to the game logic, they are pretty straight forward. The Guess
class responsible to do the matching of each guess and return result of that guess. The Game
class control the game state, determine whether the user is win or lose. Note how we provide an event
to update code that are interested in knowing the game state has changed.
4. Finally we also need to take care of player input, so we have the GameInput
class. This class is responsible for manipulating the buffer of player’s input and flush out the buffer for Game
and Guess
to consume.
5. Do not forget to config our service container for Game
and GameInput
. We will need them in our UI. In Program.cs
add the following lines:
builder.Services.AddSingleton<Game>();
builder.Services.AddSingleton<GameInput>();
The complete Program.cs
should look like this:
That’s all for the game logic. Quite simple isn’t it? 😎
The Game UI with Blazor
- Clean up project and removing unused code.
Return to our project structure:
Since we only care about the game, most of the generated files in default project setup are not needed.
For Pages
, keep only Index.razor
For Shared
, keep only MainLayout.razor
and MainLayout.razor.css
For wwwroot
, remove sample-data
Open MainLayout.cs
and remove everything and leave only these lines:
<div class="page">
<main>@Body</main>
</div>
because we don’t need a side bar in our project.
2. Build player input UI
Create Input
folder under Pages
directory. Then In Input
folder create new files KeyboardButton.razor
and KeyboardButton.razor.css
:
In Blazor, you can add CSS style to app.css
to make a style globally available. If you create a CSS file named after a component (e.g. component.razor.css
), the styles defined in the file will available to that component only (scoped). We call this CSS isolation.
Remember we did configured the service container to return the same Game
object when requested? The first line @inject Game _game
tells Blazor that when creating this component, inject an instance of Game
so we can access the properties and methods of it.
In Blazor when we annotated a property with [Parameter]
attribute, then this parameter can be set when other component contains this component. For example, assuming there is another component and it contains a KeyboardButton
then you can set the parameters like this:
<KeyboardButton Text="A" OnClick="@(_ => Console.WriteLine(key))"/>
You can also reference code in markup by prepending @
.
There are 2 points in Keyboard.razor
worth we to look at.
- You can reference an HTML element in Blazor code via creating a variable of type
ElementReference
and use@ref
attribute to map the HTML element to the said variable. - By setting focus to the container
div
tag after component finished rendering and set the focus back to the container whenever focus is lost, we can ensure to catch all player keypress event.
Now we have a player input UI as below:
3. Build the game display
There are two types of display needed, one for showing what player inputted; One for showing the guess results. Note how we can change the CSS style at run-time.
The letter box should look like this:
4. Stitch things together.
Now we return to Index.razor
and edit it like below:
Things we can learn here:
- We use
@page "/"
here to route this page as default page. - How we listen to
Game
andGameInput
update event. - How we load
word-list.txt
and set answer of current game on page initialised inOnInitializedAsync()
method. - When we want Blazor to re-render the screen, we can call the method
StateHasChanged()
- To avoid memory leak, it is a best practice to remove the event listener when we unload/close the page. We can do this by implementing
IDisposable
interface and put the cleanup code insideDispose()
method.
Finally, we edit app.css
to add shared styles used in our game. Append these style at the bottom of app.css
:
Note: Remember to create your own word-list.txt
and copy to wwwroot
folder.
Now open your terminal/command prompt and run dotnet run
to play your game. :)
Publish Your Game
When you are ready, open your terminal/command prompt and run dotnet publish -c Release
. You can find your project files at /bin/Release/net6.0/publish/wwwroot
If you have a GitHub account, you can create a new repository and then go to Settings
page. Click Pages
on the left menu and you can now choose which branch to publish. We use the default setting this time, click Save
to finish.
Now clone the repository and then copy all files in publish/wwwroot
folder to your repository root directory. Commit the changes and push to GitHub.
Congratulations! You can now play your game at:
https://your-github-username.github.io/your-repository-name
Challenge Yourself
To test about the knowledge you gained in this project, try to add a button in Index.razor
. Display only if game is ended. When clicked the reset with a new answer.
Summary
You have just created your own Wordle like game using C# and Blazor. Now you can deployed your game to static web host and invite your friends to get it a try.
If you watch the code carefully, the game logic and UI supports:
- Variable length answer. You can even contains words of different length in your answer file. It will just work for longer/shorter word.
- You can also set how many attempts a player allowed to guess to increase/decrease difficulty.
- Start a new game upon player win/lose by clicking on the restart button. Theres no limit of game allowed per day.
- Of course you can style the game with your own taste! 🌈
Features of original Wordle that we didn’t implemented (not highly related to Blazor):
The full source code can be found on my GitHub repo, which used Mudblazor UI component library instead of bootstrap. Have fun and stay tuned for the next fun project of my #EverythingInCSharp
series! 😉