Developing Mini Games with Blazor? Strike while the iron is hot!!!

Developing Mini Games with Blazor? Strike while the iron is hot!!!

Blazor has been live for 1 day, and development feels really comfortable. Let's add another tool and a few mini games.

Last updated 6/23/2023 11:50 PM
沙漠尽头的狼
11 min read
Category
Blazor
Tags
.NET C# Blazor Dotnet9

Hello everyone, I am the Wolf at the End of the Desert.

The website has been rebuilt with Blazor and went online a day ago. Developing with Blazor is truly convenient. In my spare time, I consulted GPT and GitHub, and launched a Regular Expression Online Tester and several online mini-games, such as Tic-Tac-Toe and Minesweeper.

Below is a brief introduction. Let me know if you're interested or have any suggestions.

1. New Online Mini Tools

1.1. Regular Expression Online Tester

Online access: https://dotnet9.com/tools/regextester

This example demonstrates how to develop a simple regular expression online verification tool using Blazor Server. Users can input a regular expression and a test string, then click the "Test" button to verify if the regex matches the test string. Additionally, this example provides over a dozen commonly used regex patterns. Users can click a link to load test data, which automatically fills in the regex and test string.

Brief explanation of the annotations in the image:

  1. Common regular expressions: Click to automatically fill in the corresponding regex (annotation 2) and test text (annotation 3). Click [Test] (annotation 4) to verify.
  2. Regular expression: Enter the regex to be used.
  3. Test text area: Fill in the string to be validated/extracted here.
  4. [Test] button: Click to apply the regex above (annotation 2) and extract the content from the test text area (annotation 3). The match results are displayed below (annotation 5).
  5. Display matching results.

The code is only 123 lines and includes commonly used regular expressions, simple enough:

@page "/tools/regextester"
@using System.Text.RegularExpressions

<PageTitle>@_title</PageTitle>

<MApp>
    <h2 style="margin-bottom: 30px; margin-top: 10px; text-align: center;">@_title</h2>


    <h3>Common Regular Expression Tests</h3>

    <p>
        @foreach (var item in _regexPatterns)
        {
            <MButton Color="lime" Class="ma-2" OnClick='() => LoadTest(item.Pattern)'>@item.Name</MButton>
        }
    </p>

    <div>
        <MTextField Label="Regular Expression" Type="string" TValue="string" @bind-Value="_regexPattern"/>
    </div>

    <div>
        <MTextarea BackgroundColor="grey lighten-2" Solo
                   Color="orange orange-darken-4" TValue="string" @bind-Value="_testString"
                   Label="Test String" Rows="8" style="font-size:12px;" RowHeight="15" AutoGrow/>
    </div>

    <div>
        <MButton Color="success" class="ma-2" OnClick="TestRegex">Test</MButton>
    </div>

    <div>
        @if (string.IsNullOrEmpty(_regexPattern) || string.IsNullOrEmpty(_testString))
        {
            <p>Please enter a regex and test string, then click the "Test" button.</p>
        }
        else
        {
            <p>Match results: </p>
            <ul>
                @foreach (var match in _matches)
                {
                    <li>@match</li>
                }
            </ul>
        }
    </div>
</MApp>

@code {
        private const string? _title = "Toolbox - Regular Expression Online Tester";
    private string? _regexPattern;
    private string? _testString;

    private string? _defaultString = @"Below are some test examples:
    history: v1.0 Regex Tester went online
    v1.1 2023-06-23 Just went online
    1. As of now, the longest domain suffix is .cancerresearch
    demo@qq.com
    dotnet9-9@vip.qq.com
    dotnet9-9@gmail.com
    demo@live.com
    127.0.0.1
    http://dotnet9.com/
    510112199901013592
    https://dotnet9.com/
    123456789012345
    18628035382
    13493532389
    川AAA008
    京B45698
    14:22:19";

    private readonly List<RegexItem> _regexPatterns = new()
    {
        new("Match Email", @"\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}"),
        new("Match Chinese", @"[\u4e00-\u9fa5]+"),
        new("Match Double-byte Characters (including Chinese)", @"[^\x00-\xff]+"),
        new("Match Time (HH:mm:ss)", @"([01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d"),
        new("Match IP (IPv4)", @"\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}"),
        new("Match ID Card", @"\d{17}[0-9Xx]|\d{15}"),
        new("Match Date (yyyy-MM-dd)", @"(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)"),
        new("Match Positive Integer", @"[1-9]\d*"),
        new("Match Negative Integer", @"-[1-9]\d*"),
        new("Match Phone Number", @"(13\d|14[579]|15[^4\D]|17[^49\D]|18\d)\d{8}"),
        new("Match License Plate", @"(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))")
    };

    private readonly List<string> _matches = new();

    private void TestRegex()
    {
        if (string.IsNullOrWhiteSpace(_regexPattern) || string.IsNullOrWhiteSpace(_testString))
        {
            return;
        }
        try
        {
            var regex = new Regex(_regexPattern);
            var match = regex.Match(_testString);
            _matches.Clear();
            while (match.Success)
            {
                _matches.Add(match.Value);
                match = match.NextMatch();
            }
        }
        catch
        {
            _matches.Clear();
        }
    }

    private void LoadTest(string pattern)
    {
        _regexPattern = pattern;
        _testString = _defaultString;
    }

    record RegexItem(string Name, string Pattern);

}

2. Online Mini Games Launched

Let me clarify first: The site owner launched these mini games solely to test the server load. If you develop games, it's recommended to use the client-side mode (WASM), because the former puts pressure on the server, while the latter puts pressure on the user's side.

2.1. Guess the Number Game

Online access: https://dotnet9.com/games/guessing-numbers

At the start of the game, a random target number between 1 and 100 is generated. Players need to guess the target number by inputting numbers. The system gives hints based on the player's guess. If the player guesses correctly, the game ends, a congratulatory message is displayed, and a button to start a new game appears.

This game is simple, with just a few dozen lines of code:

@page "/games/guessing-numbers"

<PageTitle>@_title</PageTitle>

<MApp>
    <h2 style="margin-bottom: 30px; margin-top: 10px; text-align: center;">@_title</h2>

    @if (_isGameOver)
    {
        <p>@_message</p>
        <p>Game Over!</p>
        <p>
            <MButton Color="lime" Class="ma-2" OnClick='StartNewGame'>Start New Game</MButton>
        </p>
    }
    else
    {
        <p>Guess a number between 1 and 100:</p>
        <p>
            <MTextField Label="Number" Type="string" TValue="int" @bind-Value="_guess"/>
        </p>
        <p>
            <MButton Color="success" class="ma-2" OnClick="CheckGuess">Guess!</MButton>
        </p>
        <p>@_message</p>
    }
</MApp>


@code {
    readonly string? _title = "Guess the Number Game";
    private int _targetNumber;
    private int _guess;
    private string? _message;
    private bool _isGameOver;

    protected override void OnInitialized()
    {
        StartNewGame();
    }

    private void StartNewGame()
    {
        var random = new Random();
        _targetNumber = random.Next(1, 101);
        _guess = 0;
        _message = "";
        _isGameOver = false;
    }

    private void CheckGuess()
    {
        if (_guess == _targetNumber)
        {
            _message = "Congratulations, you guessed it!";
            _isGameOver = true;
        }
        else if (_guess < _targetNumber)
        {
            _message = "Too low!";
        }
        else
        {
            _message = "Too high!";
        }
    }

}

2.2. Tic-Tac-Toe Game

Online access: https://dotnet9.com/games/tictactoe

A simple Tic-Tac-Toe game. Players can click on squares on the board to make a move. The game checks if a player has won or if it's a draw, and displays the appropriate message when the game ends. Players can click the "Start New Game" button to restart.

Code: 117 lines.

@page "/games/tictactoe"

<PageTitle>@_title</PageTitle>

<MApp>
    <h2 style="margin-bottom: 30px; margin-top: 10px; text-align: center;">@_title</h2>

    @if (_isGameOver)
    {
        <p>@_message</p>
        <p>Game Over!</p>
        <MButton Color="lime" Class="ma-2" OnClick='StartNewGame'>Start New Game</MButton>
    }
    else
    {
        <div style="text-align: center;">
            @for (var i = 0; i < 3; i++)
            {
                <div>
                    @for (var j = 0; j < 3; j++)
                    {
                        var i1 = i * 3 + j;
                        <MButton Color="lime" OnClick='() => MakeMove(i1)' Disabled='IsCellDisabled(i1)'>@_board[i1]</MButton>
                    }
                </div>
            }
        </div>
        <p>@_message</p>
    }
</MApp>

@code {
    readonly string? _title = "Tic-Tac-Toe Game";
    private string?[] _board = new string[9];
    private string _currentPlayer = "X";
    private string? _message;
    private bool _isGameOver;

    private void MakeMove(int index)
    {
        if (_board[index] != null || _isGameOver)
        {
            return;
        }
        _board[index] = _currentPlayer;
        if (CheckWin(_currentPlayer))
        {
            _message = $"Player {_currentPlayer} wins!";
            _isGameOver = true;
        }
        else if (_board.All(cell => cell != null))
        {
            _message = "Draw!";
            _isGameOver = true;
        }
        else
        {
            _currentPlayer = _currentPlayer == "X" ? "O" : "X";
            if (_currentPlayer == "O")
            {
                MakeComputerMove();
            }
        }
    }

    private void MakeComputerMove()
    {
    // Simple random selection of an available square to make a move
        var availableMoves = Enumerable.Range(0, 9).Where(i => _board[i] == null).ToList();
        var random = new Random();
        var randomIndex = random.Next(availableMoves.Count);
        var computerMove = availableMoves[randomIndex];
        _board[computerMove] = _currentPlayer;

        if (CheckWin(_currentPlayer))
        {
            _message = $"Computer wins!";
            _isGameOver = true;
        }
        else if (_board.All(cell => cell != null))
        {
            _message = "Draw!";
            _isGameOver = true;
        }
        else
        {
            _currentPlayer = _currentPlayer == "X" ? "O" : "X";
        }
    }

    private bool CheckWin(string player)
    {
    // Check all possible winning combinations
        return (_board[0] == player && _board[1] == player && _board[2] == player) ||
               (_board[3] == player && _board[4] == player && _board[5] == player) ||
               (_board[6] == player && _board[7] == player && _board[8] == player) ||
               (_board[0] == player && _board[3] == player && _board[6] == player) ||
               (_board[1] == player && _board[4] == player && _board[7] == player) ||
               (_board[2] == player && _board[5] == player && _board[8] == player) ||
               (_board[0] == player && _board[4] == player && _board[8] == player) ||
               (_board[2] == player && _board[4] == player && _board[6] == player);
    }

    private bool IsCellDisabled(int index)
    {
        return _isGameOver || _board[index] != null;
    }

    private void StartNewGame()
    {
        _board = new string[9];
        _currentPlayer = "X";
        _message = null;
        _isGameOver = false;
    }

}

2.3. Online Minesweeper Game

Online access: https://dotnet9.com/games/minesweeper

In this example, players click squares to reveal them. If a player steps on a mine, the game ends. If the revealed square has mines adjacent to it, the square displays a number indicating the count of surrounding mines. If the player successfully reveals all squares without mines, the game is won.

This game highly resembles the classic Windows version of Minesweeper. The code is more extensive and is adapted from the open-source project: https://github.com/jarDotNet/BlazorMinesweeper. If you're interested in the code, check out that project or read the Minesweeper-related code on the Dotnet9 website:

3. Final Words

Let me reiterate: The mini games on the site are only for testing. The Server mode is not recommended for developing game-like features; that should be left to the Client mode.

If you have any tool requirements, feel free to leave a comment. The site owner will consider adding them when time permits.

That's all for today. Wishing everyone a peaceful Dragon Boat Festival.

Keep Exploring

Related Reading

More Articles
Same category / Same tag 6/23/2023

Dotnet9 Website Returns to Blazor Refactoring, Access Speed Soars, and Interaction is More Convenient!

Originally, the site owner aimed to experience .NET 8 Blazor Web App by adding Razor components to Razor Pages. However, the hybrid mode Razor components currently cannot interact, and the page also shows a reconnection gray UI. So, they simply used Blazor Server for refactoring. After days of hard work, the website's front-end has been completely replaced with Blazor Server over Razor Pages, and the annoying reconnection issue has been resolved. Now the website loads very fast, though it might be an illusion, but personally it feels great.

Continue Reading
Same category / Same tag 11/6/2024

Why My Blog Website Returned to Blazor

The development of the blog website has gone through many hardships, with nearly 10 versions including MVC, Vue, Go, etc. Now it has returned to Blazor and adopted static SSR, resulting in a significant speed increase and successful launch.

Continue Reading
Same category / Same tag 2/29/2024

Data Display Can Also Be Done Like This in Winform

In the process of developing Winform, data display functionality is often required. Previously, the gridcontrol control was commonly used. Today, through an example, I would like to introduce how to use the table component from Ant Design Blazor for data display in a Winform Blazor Hybrid application.

Continue Reading
Same category / Same tag 2/29/2024

Can the Winform interface also look good?

A few days ago, I introduced using Blazor Hybrid in Winform, and mentioned that with the Blazor UI, our Winform programs can be designed to look better. Next, I will illustrate with an example of drawing in Winform Blazor Hybrid, hoping it helps you.

Continue Reading