Today is the Fifth day of the fifth lunar month, the Dragon Boat Festival. Here, I wish you all a healthy Dragon Boat Festival!
The Dragon Boat Festival is one of the ancient Chinese traditional festivals. Dragon Boat Festival is also known as Dragon Boat Five, Duanyang. In addition, the Dragon Boat Festival has many other names, such as: Noon festival, double five festival, May Festival, bath Orchid festival, girls festival, day festival, to wax, poets festival, dragon day and so on.
I’m sorry. I’m off topic. Let’s stop.
What happened is this, this year the Dragon Boat Festival company to each employee have prepared a zongzi gift box, this has been a few zongzi, did not expect this year’s zongzi gift box hidden porch, with a chessboard and five pieces.
Zongzi is not important, mainly this backgammon I quite like, ha ha ha. 😎
While I was reconstructing my blog with Blazor, I wondered if I could write a backgammon ⚫⚪ mini-game with Blazor.
Here is a sample game of gobang based on Blazor: blazor.meowv.com/gobang.
You can open the link and let it load for a while (it’s on GitHub and a bit slow ~🤪), then come back to the article.
At first I wrote it myself, but the more I wrote it, the more complex it became, so I gave it up and looked up Github to see if I had ever implemented a similar requirement, not to mention that there was a Blazor that actually implemented it: github.com/ut32/gobang… So my code logic basically refers to the god’s code. 👍 👍 👍
Create a new gobang. RazorRazor component and set the route to @page “/ Gobang “.
If you haven’t read my Blazor articles, please go ahead and read them. 😁
I believe that everyone has played backgammon, I will not say the rules.
Let’s start with the requirements and implementation steps:
- Displays a 19×19 checkerboard on the page.
- Given two choices, computer first or me first.
- Start game button, end game button, a button, text dynamic display.
- The problem, the sunspot always first hand, black and white alternate fall, has fallen place is not allowed to continue to fall.
- Black and white chess pieces fall style problem.
- Man-machine war, computer how to choose the best position to fall.
- How to judge winning or losing, four directions: horizontal or vertical.
- To achieve a simple backgammon small game, do not consider to give up, hand ban and other issues.
Start by rendering a 19×19 checkerboard with two for loops and 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>
Copy the code
One of the onclick methods not to look at the main is our child click event.
Chess is a two-dimensional array defined: private int[,] Chess = new int[19, 19]; .
The most important piece is the SPAN tag, which uses class to control black and white, when class = “chess1” is black and when class = “chess2” is white.
At the same time, add some buttons next to the board to select the first option and description information.
<div class="chess-info">
<h1>Gobang ⚫ ⚪</h1>
<p><b>⚡ it's time to show real technology, let's have a man-machine war ⚡</b></p>
<p><label><input type="radio" name="chess" checked="checked" @onclick="@(() => first = "ai") ">The computer solution</label></p>
<p><label><input type="radio" name="chess" @onclick="@(() => first = "me") ">I lived</label></p>
<p><button class="box-btn" @onclick="StartGame">@(IsInGame ? "End game" : "start game ")</button></p>
<div class="chess-msg">
<p><b>@msgs</b></p>
<p>Rules of the game:</p>
<span>(1) Please choose computer first or you first, black always 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) Each side holds a piece of the same color.</span>
<span>(5) Empty checkerboard opening.</span>
<span>(6) black first, white, alternate down time, each time only a son.</span>
<span>(7) A chess piece is placed on a blank point of the board. After a chess piece is made up, it shall not move to any other point, be removed from the board or be picked up and dropped elsewhere.</span>
<span>(8) The first piece of the black side can be placed at any intersection of the board.</span>
<span>(9) It is the right of both parties to take turns,<del>But either party is allowed to waive the right to PASS.</del>.</span>
<span>(10)<del>In the backgammon match, the black side shall specify the opening, three hands can be exchanged, and five hands can play two. Throughout the game, black has barred hands and white has no barred hands. Black hand ban has three three ban hand, four ban hand and long ban hand three.</del></span>
</div>
</div>
Copy the code
I’m also going to give you the CSS styles that I’m going to use.
.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(# 445%, #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;
}
Copy the code
Now let’s bring in some of the variables and methods that we’re going to use.
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;
Copy the code
Chess is a two-dimensional array of checkerboards.
First is the first field, the default is computer first, I assign the value “ai”, using it to determine whether it is me or computer first.
IsInGame is used to judge the current state of the game, whether to start the game, according to it to dynamic control button text content.
An MSGS is a message that tells the player what is going on between the two parties.
AIChess = 1 and MineChess = 2 are black and white. By default, the computer is black and I am white.
The top two radio tags are used to select the first player. Click the event to assign values to first and click the event StartGame.
private void StartGame()
{
// Initialize the checkerboard
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 first
if (first == "ai")
{
AIChess = 1;
MineChess = 2;
// The computer is located in the center of the sky
Chess[9.9] = AIChess;
msgs = "Computer: Black son ⚫ me: white son ⚪";
}
else
{
// My first hand is my black, computer white
MineChess = 1;
AIChess = 2;
msgs = "Me: Black son ⚫ Computer: white son ⚪"; }}// Change the state of the game, used to display different text buttonsIsInGame = ! IsInGame; }Copy the code
Before starting the game, first initialize the board, and then determine whether the current in the game, in the game click the button corresponding to the end of the game, so this time will prompt message empty. If the game did not start, click the button is to start the game, at this time to judge the computer or my first hand. According to these two cases, assign values to AIChess and MineChess respectively, and give corresponding prompt messages. If it is the computer first, then automatically in the chessboard is the center of the location of the child, check the position called day yuan. Chess[9, 9] = AIChess; IsInGame=! IsInGame=! IsInGame; .
So if it is my hand or the computer after the game, we need to play the game, then the method of our game is Playing(int row, int cell).
private async Task Playing(int row, int cell)
{
// Whether to start the game, the current judgment does not give a hint
if(! IsInGame) {await Common.InvokeAsync("alert"."\n💪 Click the start button to open the game, please read the rules of the game 💪");
return;
}
// No operation is performed
if(Chess[row, cell] ! =0)
return;
// Use the coordinates passed in
Chess[row, cell] = MineChess;
if (IsWin(MineChess, row, cell))
{
await Common.InvokeAsync("alert"."\n Congratulations, you have won 👍"); IsInGame = ! IsInGame;return;
}
// The computer falls after we fall
await AIPlaying(AIChess);
}
Copy the code
I judge whether to start the game before I put the game down. If I click the start game button, I will give a pop-up prompt and directly return without doing any operation. Then, we click the position where the game has been placed and directly return without doing any operation.
Chess[row, cell] == 0 indicates that the position is unmatched. Chess[row, cell]! Theta equals 0 means we’ve already taken the marbles, so we can’t take any more marbles here.
Chess[row, cell] = MineChess; Chess[row, cell] = MineChess; .
A new method IsWin(…) is introduced here. Later said. If true is a win, give a hint and change the game state. If we do not win, the computer will play after we win, and a new method is introduced here: AIPlaying(…) .
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 positions that have not yet been dropped
if (Chess[i, j] == 0)
{
minePoints.Add(GetValuedPoint(chess, i, j));
aiPonints.Add(GetValuedPoint((chess == 1 ? 2 : 1), i, j)); }}}// Get the best position
var minePoint = minePoints.OrderByDescending(x => x.Score).FirstOrDefault();
var aiPonint = aiPonints.OrderByDescending(x => x.Score).FirstOrDefault();
if(minePoint ! =null&& aiPonint ! =null)
{
// If a position is higher than our opponent's score, we will 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; }}}}Copy the code
The computer uses the traversal scoring method to calculate the score of each vacancy from high to the end, so it first constructs an object ValuedPoint.
//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; }}Copy the code
Add our and computer scoring object list: minePoints and aiPonints, calculate the score by traversing the checkerboard position of the undecided child, and introduce a new method to calculate the score strategy: GetValuedPoint(…) .
Then get the best position of the spots and white spots respectively, that is, get the coordinates of the position with the highest score. For the computer spots, if my score is higher than the computer, the computer will preempt this position for the spots.
IsWin(…) is also called after the drop. To determine if the computer has won, and if it has won, it will give a hint to change the state and end the game, and if it has not won, it will continue.
Now look at the scoring strategy: GetValuedPoint(…) .
Click to see 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;
#regionTransverse 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
#regionVertical 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
#regionPrime 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
#regionSi 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
}
};
}
Copy the code
Pass through the chesses in the given position in four directions: horizontal nod ⬅, vertical nod, ascending stroke, downward slope, ↗, respectively, and calculate the score of each empty seat from high to low. Finally return ValuedPoint object.
IsWin(int chess, int row, int cell) IsWin(int chess, int row, int cell)
private bool IsWin(int chess, int row, int cell)
{
#regionTransverse 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
#regionVertical 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
#regionPrime 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
#regionSi 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;
}
Copy the code
After the two sides of the chess match score, based on the coordinates of the score, find out whether there are five consecutive matches in the horizontal direction ⬅, vertical direction depressing sibeko, passing direction depressing, suppressed direction ↗, equinox. If you can find it, return true, indicating that you have won. End the game, if not, continue the match.
This is how to develop a small game based on Blazor ⚫⚪. Please enjoy it. Finally, wish you a healthy Dragon Boat Festival!
** good I can’t write again, my girlfriend called me to play gobang ⚫⚪. * * 🤭 🤭 🤭