Development of Gobang based on Blazor ⚫βšͺ Games

Posted by Ancoats on Fri, 26 Jun 2020 09:14:15 +0200

Today is the Dragon Boat Festival on the fifth day of may in the lunar calendar. Here, I wish you all a happy Dragon Boat Festival!

Dragon Boat Festival is one of the ancient traditional festivals of the Chinese nation. Dragon Boat Festival is also called Duan five, Dragon Boat Festival. In addition, Dragon Boat Festival also has many other names, such as: noon day, heavy five, may day, Yulan day, daughter's day, Tianzhong day, Dila, poet's day, dragon day and so on.

I'm sorry. I'm off the subject. Let's stop.

This year's Dragon Boat Festival company has prepared a zongzi gift box for every employee. This year's zongzi gift box has only a few zongzi. I didn't expect that there is a secret inside the zongzi gift box this year, with a chess board and five pieces inside.

Zongzi doesn't matter. It's mainly the Gobang that I like very much. Hahaha. 😎

At that time, I used razor to reconstruct the previous blog, so I wondered if I could write a Gobang with razor ⚫βšͺ What about games?

Let's do what we say. This article is mainly to share the Gobang games developed based on Blazor. First, let's put the trial address: https://blazor.meowv.com/gobang .

You can open the link first and let him load it for a while~ πŸ€ͺ ), and then go back to the article.

At first, I wrote it by myself. I found that the more complicated it was, the more complicated it was. So I gave up and looked for Github to see if it had ever realized similar needs. Let alone a great God who had implemented it with Blazor. Address: https://github.com/ut32/gobang/ , so my code logic basically refers to the code of the God. πŸ‘πŸ‘πŸ‘

Let's take a look at the implementation process and create a new one Gobang.razorrazor Component, set route: @ page "/gobang".

I put it here directly in the previous projects of the Blazor practical series. If you haven't read my Blazor practical series, I suggest you brush it quickly. 😁

I believe that everyone has played Gobang, I will not say the rules.

First, the requirements and implementation steps:

  1. Display a 19x19 chessboard on the page.
  2. Give me two options. Computer first or me first.
  3. Start game button, end game button, one button, text dynamic display.
  4. As for the problem of falling seeds, the black ones are always the first to fall, and the black and white ones alternate to fall seeds. It is not allowed to continue to fall seeds where they have already fallen.
  5. The pattern of black and white pieces.
  6. Man machine battle, how to choose the best position for the computer.
  7. How to judge the winning and losing, four directions: horizontal and vertical control.
  8. To realize a simple Gobang game, we don't think about giving up the drop and forbidding the hand.

First, render a 19x19 chessboard, directly two layers for loop with CSS.

<div class="gobang-box">
    <div class="chess">
        @for (var i = 0; i < 19; i++)
        {
            @for (var j = 0; j < 19; j++)
            {
                var _i = i;
                var _j = j;
                <div class="cell" @onclick="@(async () => await Playing(_i, _j))">
                    <span class="chess@(Chess[i, j])"></span>
                </div>
            }
        }
    </div>
</div>

The onclick method doesn't look at first. It's mainly the click event of our party.

Chess is a two-dimensional array defined: private int[,] Chess = new int[19, 19];.

The most important chess piece is span tag, which uses class to control black and white. When class = "chess1" is black, when class = "chess2" is white.

At the same time, add some buttons beside the chessboard to select the option and description information of the first player.

<div class="chess-info">
    <h1>Gobang ⚫βšͺ </h1>
    <p><b> A kind of It's time to show real technology. Let's have a man-machine battle A kind of </b></p>
    <p>< label > < input type = "radio" name = "chess" checked = "checked" @ onclick = "@ (() = > first =" Ai ")" > Computer forerunner < / label ></p>
    <p>< label > < input type = "radio" name = "chess" @ onclick = "@ (() = > first =" me ")" > I'll start with < / label ></p>
    <p>< button class = "box BTN" @ onclick = "startgame" ></p>
    <div class="chess-msg">
        <p><b>@msgs</b></p>
        <p>Rules of the game:</p>
        <span>(1) Please choose computer forerunner or you forerunner. Black chess is always the first. </span>
        <span>(2) Click the start game button to start the match. </span>
        <span>(3) Click the end game button to end the match. </span>
        <span>(4) Both sides of the game hold the same color. </span>
        <span>(5) Empty chessboard starts. </span>
        <span>(6) Black first, white then, alternately, only one child at a time. </span>
        <span>(7) The chess pieces are placed on the blank points of the chessboard. After the pieces are settled, they shall not move to other points, or take them off the chessboard or pick them up to fall elsewhere. </span>
        <span>(8) The first piece of black square can be placed at any intersection of chessboard. </span>
        <span>(9) Taking turns is the right of both parties, < del > but allows either party to give up the right of PASS < / del >. </span>
        <span>(10) < del > the game of Gobang should start with the black side's designation, three hands can be exchanged and five hands can play two games. During the whole match, the black side has forbidden hands, while the white side has no forbidden hands. There are three kinds of forbidden hands: three three forbidden hands, four forbidden hands and long chain forbidden hands. </del></span>
    </div>
</div>

At the same time, I will give you the css style.

.gobang-box {
    width: 1200px;
    margin: 0 auto;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
.chess {
    width: 760px;
    height: 760px;
    float: left;
}
.chess .cell {
    float: left;
    width: 40px;
    height: 40px;
    position: relative;
    cursor: pointer;
    font-size: 10px;
    color: #ffd800;
}
.chess .cell::after {
    content:' ';
    position: absolute;
    height: 2px;
    display: block;
    width: 100%;
    border-bottom: #f5d099 1px solid;
    background: #c8a06f;
    top: 50%;
    left: 0;
    z-index: 2;
}
.chess .cell::before {
    content:' ';
    position: absolute;
    height: 100%;
    display: block;
    width: 2px;
    border-right: #f5d099 1px solid;
    background: #c8a06f;
    top: 0;
    left: 50%;
    z-index: 1;
}
.chess .cell .chess1 {
    display: block;
    width: 30px;
    height: 30px;
    border-radius: 15px;
    text-align: center;
    line-height: 54px;
    background: #000000;
    left: 5px;
    top: 5px;
    position: absolute;
    z-index: 10;
    background-image: radial-gradient(#444 5%, #111 15%, #000 60%);
    box-shadow: 0px 0px 3px #333;
}
.chess .cell .chess2 {
    display: block;
    width: 30px;
    height: 30px;
    border-radius: 15px;
    text-align: center;
    left: 5px;
    top: 5px;
    position: absolute;
    z-index: 10;
    line-height: 54px;
    background-image: radial-gradient(#ffffff 5%, #f1f1f1 15%, #f1f1f1 60%);
    box-shadow: 0px 0px 3px #333;
}
.chess-info {
    float: left;
    width: 400px;
    height: 760px;
    padding-left: 20px;
    margin-left: 40px;
}
.chess-info input {
    display: initial;
    width: initial;
    height: initial;
    visibility: initial;
}
.chess-msg {
    margin-top: 20px;
    color: #aaa;
}
.chess-msg span {
    display: block;
    font-size: 12px;
}

Now let's bring in some of the variables and methods used.

private int[,] Chess = new int[19, 19];

private string first = "ai";

private bool IsInGame = false;

private string msgs;

private int AIChess = 1;

private int MineChess = 2;

Chess is a two-dimensional array of chessboards.

First is the first field. The default is computer first. I assign "ai" here to judge whether it is my first or computer first.

IsInGame is used to judge the current game status and whether to start the game. According to it, you can dynamically control the text content of the button.

msgs is a prompt message that tells players about the situation of both sides.

AIChess = 1 and MineChess = 2 are black and white. The default computer is black and I am white.

The two upper radio tabs are used to select the first player. Click the event to assign the first value, and click the event StartGame.

private void StartGame()
{
    // Initialize chessboard
    Chess = new int[19, 19];

    // Whether to start the game, click the button to reset the display message
    if (IsInGame)
    {
        msgs = string.Empty;
    }
    else
    {
        // Computer forerunner
        if (first == "ai")
        {
            AIChess = 1;
            MineChess = 2;

            // Location of Tianyuan in the center of the computer
            Chess[9, 9] = AIChess;

            msgs = "Computer: holding black ⚫ Me: zhibaizi βšͺ";
        }
        else
        {
            // If I'm the first one, I'm the black one, and the computer is the white one
            MineChess = 1;
            AIChess = 2;

            msgs = "Me: black man ⚫ Computer: zhibaizi βšͺ";
        }
    }

    // Change game status, buttons for displaying different text
    IsInGame = !IsInGame;
}

Before starting the game, initialize the chessboard first, and then judge whether it is in the game. If you click the button in the game, you must end the game. Then the prompt message will be cleared. If you haven't started the game, click the button to start the match. At this time, judge whether the computer pioneer or I pioneer. According to the two cases, AIChess and MineChess are assigned values and corresponding prompt messages are given. If it's a computer forerunner, then it will automatically drop in the center of the chessboard, and check the location called Tianyuan. Directly assign Chess[9, 9] = AIChess to the chessboard array, that is to say, if you click the last button, you need to change the state: isingame =! Isingame;.

Then, if it's after I start or the computer ends, and we need to start, then the way we start is Playing(int row, int cell) method.

private async Task Playing(int row, int cell)
{
    // Whether to start the game? The current judgment does not give a prompt
    if (!IsInGame)
    {
        await Common.InvokeAsync("alert", "\nπŸ’ͺClick the start game button to start the match. Please read the rules of the gameπŸ’ͺ");
        return;
    }

    // Directly return the dropped child without any operation
    if (Chess[row, cell] != 0)
        return;

    // According to the coordinates passed in, we will finish the settlement
    Chess[row, cell] = MineChess;

    if (IsWin(MineChess, row, cell))
    {
        await Common.InvokeAsync("alert", "\n Congratulations, you wonπŸ‘");
        IsInGame = !IsInGame;
        return;
    }

    // After we settled down, the computer went down
    await AIPlaying(AIChess);
}

Before I put the drop, I will judge whether to start the game first. If I click the start game button, I will give a pop-up prompt and return directly without any operation. Then there is a situation where we click the position where the drop has been made, and return directly without any operation.

It can be judged whether a position has fallen or not according to the coordinates passed in. Chess[row, cell] == 0 means that it has not fallen, chess [row, cell]! = 0 means that it has already fallen, so it is not allowed to continue falling here.

Then we can place the position we clicked on, and assign a value to the array directly: Chess[row, cell] = MineChess;.

After the fall, we need to judge whether we win or not. A new method IsWin(...) is introduced here. If you return true, you win. Give a prompt to change the game status. If we don't win, it's time for our computer to go. Here is a new method: AI playing (...).

private async Task AIPlaying(int chess)
{
    // we
    var minePoints = new List<ValuedPoint>();
    // computer
    var aiPonints = new List<ValuedPoint>();

    for (int i = 0; i < 19; i++)
    {
        for (int j = 0; j < 19; j++)
        {
            // List of locations not yet settled
            if (Chess[i, j] == 0)
            {
                minePoints.Add(GetValuedPoint(chess, i, j));

                aiPonints.Add(GetValuedPoint((chess == 1 ? 2 : 1), i, j));
            }
        }
    }

    // Get the best location
    var minePoint = minePoints.OrderByDescending(x => x.Score).FirstOrDefault();
    var aiPonint = aiPonints.OrderByDescending(x => x.Score).FirstOrDefault();

    if (minePoint != null && aiPonint != null)
    {
        // If the opponent's score in a position is higher than ours, then seize the position
        if (minePoint.Score > aiPonint.Score)
        {
            Chess[minePoint.Point.Row, minePoint.Point.Cell] = chess;

            if (IsWin(AIChess, minePoint.Point.Row, minePoint.Point.Cell))
            {
                await Common.InvokeAsync("alert", "\n Computer wins, you scumπŸ‘Ž");
                IsInGame = !IsInGame;
                return;
            }
        }
        else
        {
            Chess[aiPonint.Point.Row, aiPonint.Point.Cell] = chess;

            if (IsWin(AIChess, aiPonint.Point.Row, aiPonint.Point.Cell))
            {
                await Common.InvokeAsync("alert", "\n Computer wins, you scumπŸ‘Ž");
                IsInGame = !IsInGame;
                return;
            }
        }
    }
}

The computer uses the ergodic scoring method to calculate the score of each vacancy. The score is from the highest to the lowest, so as to build an object ValuedPoint first.

//ValuedPoint.cs
public class ValuedPoint
{
    public Point Point { get; set; }

    public int Score { get; set; }
}

//Point.cs
public struct Point
{
    public int Row { get; set; }
    public int Cell { get; set; }
}

Add a list of our and computer scoring objects: minePoints and aiPonints, traverse the positions of the unsettled pieces in the chessboard for score calculation, and introduce a new method of score calculation strategy: GetValuedPoint(...).

Then get the best position of the black and white, that is, get the coordinates of the position with the highest score. For the computer, if my score is higher than that of the computer, the computer will seize the position to drop.

After the drop, IsWin(...) is also called to judge whether the computer wins. If it wins, it will give a prompt to change the state and end the match. If it doesn't win, it will continue.

Now let's look at the scoring strategy: GetValuedPoint(...).

Click to view the code
private ValuedPoint GetValuedPoint(int chess, int row, int cell)
{
    var aiChess = chess == 1 ? 2 : 1;

    int HScore = 0, VScore = 0, PScore = 0, LScore = 0;

    #region transverse direction βž‘β¬…

    {
        var i = 1;
        var score = 1;
        var validPlace = 0;
        var rightValid = true;
        var leftValid = true;
        var rightSpace = 0;
        var leftSpace = 0;
        var isDead = false;

        while (i < 5)
        {
            var right = cell + i;
            if (rightValid && right < 19)
            {
                if (Chess[row, right] == chess)
                {
                    if (rightSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[row, right] == 0)
                {
                    rightSpace++;
                    validPlace++;
                }
                else if (Chess[row, right] == aiChess)
                {
                    rightValid = false;
                    if (rightSpace == 0)
                        isDead = true;
                }
            }

            var left = cell - i;
            if (leftValid && left >= 0)
            {
                if (Chess[row, left] == chess)
                {
                    if (leftSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[row, left] == 0)
                {
                    leftSpace++;
                    validPlace++;
                }
                else if (Chess[row, left] == aiChess)
                {
                    leftValid = false;
                    if (leftSpace == 0)
                        isDead = true;
                }
            }

            i++;
        }

        if (score >= 5)
            HScore = 100000;

        if (score == 4)
        {
            if (!isDead)
                HScore = 80000;
            else
                HScore = validPlace <= 4 ? 0 : 8000;
        }

        if (score == 3)
        {
            if (!isDead)
                HScore = validPlace <= 4 ? 0 : 4000;
            else
                HScore = validPlace <= 4 ? 0 : 2000;
        }

        if (score == 2)
        {
            if (!isDead)
                HScore = validPlace <= 4 ? 0 : 600;
            else
                HScore = validPlace <= 4 ? 0 : 300;
        }
    }

    #endregion

    #region vertical direction ⬇⬆

    {
        var i = 1;
        var score = 1;
        var validPlace = 0;
        var topValid = true;
        var bottomValid = true;
        var topSpace = 0;
        var bottomSpace = 0;
        var isDead = false;

        while (i < 5)
        {
            var top = row - i;
            if (topValid && top >= 0)
            {
                if (Chess[top, cell] == chess)
                {
                    if (topSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[top, cell] == 0)
                {
                    topSpace++;
                    validPlace++;
                }
                else if (Chess[top, cell] == aiChess)
                {
                    topValid = false;
                    if (topSpace == 0)
                        isDead = true;
                }
            }

            var bottom = row + i;
            if (bottomValid && bottom < 19)
            {
                if (Chess[bottom, cell] == chess)
                {
                    if (bottomSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[bottom, cell] == 0)
                {
                    bottomSpace++;
                    validPlace++;
                }
                else if (Chess[bottom, cell] == aiChess)
                {
                    bottomValid = false;
                    if (bottomSpace == 0)
                        isDead = true;
                }
            }

            i++;
        }

        if (score >= 5)
            VScore = 100000;

        if (score == 4)
        {
            if (!isDead)
                VScore = 80000;
            else
                VScore = validPlace <= 4 ? 0 : 8000;
        }
        if (score == 3)
        {
            if (!isDead)
                VScore = validPlace <= 4 ? 0 : 4000;
            else
                VScore = validPlace <= 4 ? 0 : 2000;
        }
        if (score == 2)
        {
            if (!isDead)
                VScore = validPlace <= 4 ? 0 : 600;
            else
                VScore = validPlace <= 4 ? 0 : 300;
        }
    }

    #endregion

    #region direction ↙↗

    {
        var i = 1;
        var score = 1;
        var validPlace = 0;
        var topValid = true;
        var bottomValid = true;
        var topSpace = 0;
        var bottomSpace = 0;
        var isDead = false;

        while (i < 5)
        {
            var rightTopRow = row - i;
            var rightTopCell = cell + i;
            if (topValid && rightTopRow >= 0 && rightTopCell < 19)
            {
                if (Chess[rightTopRow, rightTopCell] == chess)
                {
                    if (topSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[rightTopRow, rightTopCell] == 0)
                {
                    topSpace++;
                    validPlace++;
                }
                else if (Chess[rightTopRow, rightTopCell] == aiChess)
                {
                    topValid = false;
                    if (topSpace == 0)
                        isDead = true;
                }
            }

            var leftBottomRow = row + i;
            var leftBottomCell = cell - i;
            if (bottomValid && leftBottomRow < 19 && leftBottomCell >= 0)
            {
                if (Chess[leftBottomRow, leftBottomCell] == chess)
                {
                    if (bottomSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[leftBottomRow, leftBottomCell] == 0)
                {
                    bottomSpace++;
                    validPlace++;
                }
                else if (Chess[leftBottomRow, leftBottomCell] == aiChess)
                {
                    bottomValid = false;
                    if (bottomSpace == 0)
                        isDead = true;
                }
            }

            i++;
        }

        if (score >= 5)
            PScore = 100000;

        if (score == 4)
        {
            if (!isDead)
                PScore = 80000;
            else
                PScore = validPlace <= 4 ? 0 : 9000;
        }
        if (score == 3)
        {
            if (!isDead)
                PScore = validPlace <= 4 ? 0 : 4500;
            else
                PScore = validPlace <= 4 ? 0 : 3000;
        }
        if (score == 2)
        {
            if (!isDead)
                PScore = validPlace <= 4 ? 0 : 800;
            else
                PScore = validPlace <= 4 ? 0 : 500;
        }
    }

    #endregion

    #region direction β†˜β†–

    {
        var i = 1;
        var score = 1;
        var validPlace = 0;
        var topSpace = 0;
        var bottomSpace = 0;
        var topValid = true;
        var bottomValid = true;
        var isDead = false;

        while (i < 5)
        {
            var leftTopRow = row - i;
            var leftTopCell = cell - i;
            if (topValid && leftTopRow >= 0 && leftTopCell >= 0)
            {
                if (Chess[leftTopRow, leftTopCell] == chess)
                {
                    if (topSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[leftTopRow, leftTopCell] == 0)
                {
                    topSpace++;
                    validPlace++;
                }
                else if (Chess[leftTopRow, leftTopCell] == aiChess)
                {
                    topValid = false;
                    if (topSpace == 0)
                        isDead = true;
                }
            }

            var rightBottomRow = row + i;
            var rightBottomCell = cell + i;
            if (bottomValid && rightBottomRow < 19 && rightBottomCell < 19)
            {
                if (Chess[rightBottomRow, rightBottomCell] == chess)
                {
                    if (bottomSpace == 0)
                        score++;
                    validPlace++;
                }
                else if (Chess[rightBottomRow, rightBottomCell] == 0)
                {
                    bottomSpace++;
                    validPlace++;
                }
                else if (Chess[rightBottomRow, rightBottomCell] == aiChess)
                {
                    bottomValid = false;
                    if (bottomSpace == 0)
                        isDead = true;
                }
            }

            i++;
        }

        if (score >= 5)
            LScore = 100000;

        if (score == 4)
        {
            if (!isDead)
                LScore = 80000;
            else
                LScore = validPlace <= 4 ? 0 : 9000;
        }

        if (score == 3)
        {
            if (!isDead)
                LScore = validPlace <= 4 ? 0 : 4500;
            else
                LScore = validPlace <= 4 ? 0 : 3000;
        }

        if (score == 2)
        {
            if (!isDead)
                LScore = validPlace <= 4 ? 0 : 800;
            else
                LScore = validPlace <= 4 ? 0 : 500;
        }
    }

    #endregion

    return new ValuedPoint
    {
        Score = HScore + VScore + PScore + LScore,
        Point = new Point
        {
            Row = row,
            Cell = cell
        }
    };
}

Four directions for the given position of the chess piece: transverse direction βž‘β¬… , vertical direction ⬇⬆ . skimming direction ↙↗ . pressing direction β†˜β†– Traversal is carried out to calculate the score of each vacancy. The score is from high to low, and finally the ValuedPoint object is returned.

At last, the method to judge whether to win the game is IsWin(int chess, int row, int cell).

private bool IsWin(int chess, int row, int cell)
{
    #region transverse direction βž‘β¬…

    {
            var i = 1;
            var score = 1;
            var rightValid = true;
            var leftValid = true;

            while (i <= 5)
            {
                var right = cell + i;
                if (rightValid && right < 19)
                {
                    if (Chess[row, right] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        rightValid = false;
                }

                var left = cell - i;
                if (leftValid && left >= 0)
                {
                    if (Chess[row, left] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        leftValid = false;
                }

                i++;
            }
    }

    #endregion

    #region vertical direction ⬇⬆

    {
            var i = 1;
            var score = 1;
            var topValid = true;
            var bottomValid = true;

            while (i < 5)
            {
                var top = row - i;
                if (topValid && top >= 0)
                {
                    if (Chess[top, cell] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        topValid = false;
                }

                var bottom = row + i;
                if (bottomValid && bottom < 19)
                {
                    if (Chess[bottom, cell] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                    {
                        bottomValid = false;
                    }
                }

                i++;
            }
    }

    #endregion

    #region direction ↙↗

    {
            var i = 1;
            var score = 1;
            var topValid = true;
            var bottomValid = true;

            while (i < 5)
            {
                var rightTopRow = row - i;
                var rightTopCell = cell + i;
                if (topValid && rightTopRow >= 0 && rightTopCell < 19)
                {
                    if (Chess[rightTopRow, rightTopCell] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        topValid = false;
                }

                var leftBottomRow = row + i;
                var leftBottomCell = cell - i;
                if (bottomValid && leftBottomRow < 19 && leftBottomCell >= 0)
                {
                    if (Chess[leftBottomRow, leftBottomCell] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        bottomValid = false;
                }

                i++;
            }
    }

    #endregion

    #region direction β†˜β†–

    {
            var i = 1;
            var score = 1;
            var topValid = true;
            var bottomValid = true;

            while (i < 5)
            {
                var leftTopRow = row - i;
                var leftTopCell = cell - i;
                if (topValid && leftTopRow >= 0 && leftTopCell >= 0)
                {
                    if (Chess[leftTopRow, leftTopCell] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        topValid = false;
                }

                var rightBottomRow = row + i;
                var rightBottomCell = cell + i;
                if (bottomValid && rightBottomRow < 19 && rightBottomCell < 19)
                {
                    if (Chess[rightBottomRow, rightBottomCell] == chess)
                    {
                        score++;
                        if (score >= 5)
                            return true;
                    }
                    else
                        bottomValid = false;
                }

                i++;
            }
    }

    #endregion

    return false;
}

When the chess player and the chess player fall on the chessboard, based on the coordinates of the fall, they will move in four directions: horizontal direction βž‘β¬… , vertical direction ⬇⬆ . skimming direction ↙↗ . pressing direction β†˜β†– Find out whether there are five companies. If you can find them, return true. It means you win. End the game. If you can't find them, continue playing.

The above is the development of Gobang based on Blazor ⚫βšͺ The realization process and function of the small game are relatively single. Please enjoy it. Finally, I wish you all a happy Dragon Boat Festival!

Well, I can't write anymore. My girlfriend called me to play Gobang ⚫βšͺ Yes. 🀭🀭🀭

Topics: github