使用 Copilot AI + Blazor 编一个五子棋游戏

开源地址

源码g01Gobang

demo
https://game.app1.es 用blazor auto发布的

1. 创建 Blazor web工程,选Auto

使用 Copilot AI + Blazor 编一个五子棋游戏

2. 打开 GitHub Copilot 窗口,输入提示词

使用 Blazor 编一个五子棋游戏

使用 Copilot AI + Blazor 编一个五子棋游戏

3.复制代码测试

为了方便调试, 我们先把运行模式由 InteractiveAuto 改为 InteractiveServer

打开 App.razor 编辑 两行 @rendermode="InteractiveAuto" 改为 @rendermode="InteractiveServer"

使用 Copilot AI + Blazor 编一个五子棋游戏

4.不出意外, 代码出现了意外

使用 Copilot AI + Blazor 编一个五子棋游戏

无脑使用AI修复一下

使用 Copilot AI + Blazor 编一个五子棋游戏

结果发现不是这个问题. 是逻辑代码 @onclick="() => PlacePiece(x, y)" 问题. 原因是PlacePiece闭包捕获的 x、y 是当前循环的值

既然人工发现了问题, 我们继续提醒一下AI, 尝试修复

使用 Copilot AI + Blazor 编一个五子棋游戏

最后还是没修复成功, 手动处理一下

@page "/"  <PageTitle>五子棋</PageTitle>  <h1>五子棋游戏</h1>  <div style="margin-bottom:10px;">     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : "白子")</b>     @if (winner != 0)     {         <span style="color:red;margin-left:20px;">胜者: @(winner == 1 ? "黑子" : "白子")</span>         <button class="btn btn-primary" style="margin-left:20px;" @onclick="Restart">重新开始</button>     } </div>  <table style="border-collapse:collapse;">     @for (int y = 0; y < BoardSize; y++)     {         <tr>             @for (int x = 0; x < BoardSize; x++)             {                 int _x = x;                 int _y = y;                 <td style="width:32px;height:32px;border:1px solid #888;text-align:center;vertical-align:middle;cursor:pointer;background:@GetCellBg(x, y);"                     @onclick="@(() => PlacePiece(_x, _y))">                     @if (board[x, y] == 1)                     {                         <span style="font-size:24px;color:black;">●</span>                     }                     else if (board[x, y] == 2)                     {                         <span style="font-size:24px;color:#eee;text-shadow:0 0 2px #888;">●</span>                     }                 </td>             }         </tr>     } </table>  @code {     const int BoardSize = 15;     int[,] board = new int[BoardSize, BoardSize];     int currentPlayer = 1; // 1: 黑子, 2: 白子     int winner = 0;      void PlacePiece(int x, int y)     {         // 修复:增加边界检查,防止越界         if (x < 0 || x >= BoardSize || y < 0 || y >= BoardSize)             return;         if (winner != 0 || board[x, y] != 0)             return;          board[x, y] = currentPlayer;         if (CheckWin(x, y, currentPlayer))         {             winner = currentPlayer;         }         else         {             currentPlayer = 3 - currentPlayer; // 切换玩家         }     }      bool CheckWin(int x, int y, int player)     {         int[][] directions = new int[][]         {             new int[]{1,0}, // 横             new int[]{0,1}, // 竖             new int[]{1,1}, // 斜             new int[]{1,-1} // 反斜         };         foreach (var dir in directions)         {             int count = 1;             count += CountDirection(x, y, dir[0], dir[1], player);             count += CountDirection(x, y, -dir[0], -dir[1], player);             if (count >= 5)                 return true;         }         return false;     }      int CountDirection(int x, int y, int dx, int dy, int player)     {         int count = 0;         for (int step = 1; step < 5; step++)         {             int nx = x + dx * step;             int ny = y + dy * step;             if (nx < 0 || nx >= BoardSize || ny < 0 || ny >= BoardSize)                 break;             if (board[nx, ny] == player)                 count++;             else                 break;         }         return count;     }      void Restart()     {         board = new int[BoardSize, BoardSize];         currentPlayer = 1;         winner = 0;     }      string GetCellBg(int x, int y)     {         return (x + y) % 2 == 0 ? "#f9d77e" : "#eac066";     } }  

说明:

  1. 添加 int _x = x; int _y = y; 确保闭包捕获的 _x_y 是当前循环的值,避免 Blazor 渲染时闭包变量引用错误。
  2. PlacePiece 方法开头增加边界检查,防止非法坐标导致数组越界异常。

这样可以彻底修复 PlacePiece 拿到的变量不正确和越界的问题。

5. 运行效果

使用 Copilot AI + Blazor 编一个五子棋游戏

6. 加入显示当前玩家的得分功能

你可以为每个玩家增加一个分数变量,每次有玩家获胜时将其分数加一,并在界面上显示。下面是具体实现方法:

  1. @code 区域添加分数字段:
int blackScore = 0; int whiteScore = 0; 
  1. 在玩家获胜时更新分数:
void PlacePiece(int x, int y) {     if (x < 0 || x >= BoardSize || y < 0 || y >= BoardSize)         return;     if (winner != 0 || board[x, y] != 0)         return;      board[x, y] = currentPlayer;     if (CheckWin(x, y, currentPlayer))     {         winner = currentPlayer;         if (winner == 1)             blackScore++;         else if (winner == 2)             whiteScore++;     }     else     {         currentPlayer = 3 - currentPlayer; // 切换玩家     } } 
  1. 在页面上显示分数(比如在标题下方):
<div style="margin-bottom:10px;">     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : "白子")</b>     <span style="margin-left:20px;">黑子得分: <b>@blackScore</b></span>     <span style="margin-left:20px;">白子得分: <b>@whiteScore</b></span>     @if (winner != 0)     {         <span style="color:red;margin-left:20px;">胜者: @(winner == 1 ? "黑子" : "白子")</span>         <button class="btn btn-primary" style="margin-left:20px;" @onclick="Restart">重新开始</button>     } </div> 

这样每次有玩家获胜时,分数都会自动更新并显示在页面上。

使用 Copilot AI + Blazor 编一个五子棋游戏

7. 加入重置得分功能

你可以在 Restart 方法中增加对分数的重置,或者单独添加一个“重置得分”按钮和方法。推荐做法如下:

  1. 添加重置得分方法:
void ResetScore() {     blackScore = 0;     whiteScore = 0; } 
  1. 在页面上添加按钮:
<button class="btn btn-danger" style="margin-left:20px;" @onclick="ResetScore">重置得分</button> 

建议将此按钮放在分数显示附近,例如:

<div style="margin-bottom:10px;">     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : "白子")</b>     <span style="margin-left:20px;">黑子得分: <b>@blackScore</b></span>     <span style="margin-left:20px;">白子得分: <b>@whiteScore</b></span>     <button class="btn btn-danger" style="margin-left:20px;" @onclick="ResetScore">重置得分</button>     @if (winner != 0)     {         <span style="color:red;margin-left:20px;">胜者: @(winner == 1 ? "黑子" : "白子")</span>         <button class="btn btn-primary" style="margin-left:20px;" @onclick="Restart">重新开始</button>     } </div> 

这样点击“重置得分”按钮即可将分数清零。

8. 在这个五子棋游戏中添加 AI 对手功能

在你的 Blazor 五子棋游戏中添加 AI 对手功能,常见做法是让一方(如白子)由 AI 自动落子。实现思路如下:

  1. 判断当前玩家是否为 AI:如 currentPlayer == 2 时,自动调用 AI 落子逻辑。
  2. 实现 AI 落子算法:可以先用简单的随机落子,后续可扩展为更智能的算法。
  3. 在玩家落子后自动触发 AI 落子:用 StateHasChanged() 刷新界面,必要时用 await Task.Delay() 增加落子延迟。

下面是具体实现方法(以白子为 AI):

@page "/"  <PageTitle>五子棋</PageTitle>  <h1>五子棋游戏</h1>  <div style="margin-bottom:10px;">     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : "白子(电脑)")</b>     <span style="margin-left:20px;">黑子得分: <b>@blackScore</b></span>     <span style="margin-left:20px;">白子得分: <b>@whiteScore</b></span>     <button class="btn btn-danger" style="margin-left:20px;" @onclick="ResetScore">重置得分</button>     @if (winner != 0)     {         <span style="color:red;margin-left:20px;">胜者: @(winner == 1 ? "黑子" : "白子(电脑)")</span>         <button class="btn btn-primary" style="margin-left:20px;" @onclick="Restart">重新开始</button>     } </div>  <table style="border-collapse:collapse;">     @for (int y = 0; y < BoardSize; y++)     {         <tr>             @for (int x = 0; x < BoardSize; x++)             {                 int _x = x;                 int _y = y;                 <td style="width:32px;height:32px;border:1px solid #888;text-align:center;vertical-align:middle;cursor:pointer;background:@GetCellBg(x, y);"                     @onclick="@(() => PlacePiece(_x, _y))">                     @if (board[x, y] == 1)                     {                         <span style="font-size:24px;color:black;">●</span>                     }                     else if (board[x, y] == 2)                     {                         <span style="font-size:24px;color:#eee;text-shadow:0 0 2px #888;">●</span>                     }                 </td>             }         </tr>     } </table>  @code {     const int BoardSize = 15;     int[,] board = new int[BoardSize, BoardSize];     int currentPlayer = 1; // 1: 黑子, 2: 白子(电脑)     int winner = 0;     int blackScore = 0;     int whiteScore = 0;     Random rand = new();      void PlacePiece(int x, int y)     {         if (winner != 0 || board[x, y] != 0 || currentPlayer == 2)             return;          board[x, y] = currentPlayer;         if (CheckWin(x, y, currentPlayer))         {             winner = currentPlayer;             if (winner == 1)                 blackScore++;             else if (winner == 2)                 whiteScore++;         }         else         {             currentPlayer = 2;             StateHasChanged();             _ = AITurnAsync();         }     }      async Task AITurnAsync()     {         await Task.Delay(500); // AI 落子延迟         if (winner != 0) return;          // 简单随机落子         var empty = new List<(int x, int y)>();         for (int i = 0; i < BoardSize; i++)             for (int j = 0; j < BoardSize; j++)                 if (board[i, j] == 0)                     empty.Add((i, j));         if (empty.Count == 0) return;          var (aiX, aiY) = empty[rand.Next(empty.Count)];         board[aiX, aiY] = 2;         if (CheckWin(aiX, aiY, 2))         {             winner = 2;             whiteScore++;         }         else         {             currentPlayer = 1;         }         StateHasChanged();     }      // 其余代码不变...     bool CheckWin(int x, int y, int player)     {         int[][] directions = new int[][]         {             new int[]{1,0}, new int[]{0,1}, new int[]{1,1}, new int[]{1,-1}         };         foreach (var dir in directions)         {             int count = 1;             count += CountDirection(x, y, dir[0], dir[1], player);             count += CountDirection(x, y, -dir[0], -dir[1], player);             if (count >= 5)                 return true;         }         return false;     }      int CountDirection(int x, int y, int dx, int dy, int player)     {         int count = 0;         for (int step = 1; step < 5; step++)         {             int nx = x + dx * step;             int ny = y + dy * step;             if (nx < 0 || nx >= BoardSize || ny < 0 || ny >= BoardSize)                 break;             if (board[nx, ny] == player)                 count++;             else                 break;         }         return count;     }      void Restart()     {         board = new int[BoardSize, BoardSize];         currentPlayer = 1;         winner = 0;     }      void ResetScore()     {         blackScore = 0;         whiteScore = 0;     }      string GetCellBg(int x, int y)     {         return (x + y) % 2 == 0 ? "#f9d77e" : "#eac066";     } } 

要点说明:

  • 玩家(黑子)落子后自动触发 AI(白子)落子。
  • AI 采用随机空位落子,简单易懂,后续可扩展为更智能算法。
  • 若要让 AI 先手,只需将 currentPlayer = 2 并在 OnInitializedRestart 时调用 AITurnAsync()

使用 Copilot AI + Blazor 编一个五子棋游戏

用是能用了, 就是这个AI太傻了,继续优化一下

要优化五子棋 AI 的落子策略,可以采用“优先级评分法”来提升难度。基本思路如下:

  1. 优先考虑能直接获胜的位置(如 AI 连成五子)。
  2. 阻止玩家获胜(如玩家有四连,AI 必须堵住)。
  3. 优先进攻(如自己有三连、二连)。
  4. 其次防守(如玩家有三连、二连)。
  5. 否则随机或选择评分最高的位置

下面是一个简单的评分法实现,AI 会优先选择能赢、能防守、能进攻的位置:

async Task AITurnAsync() {     await Task.Delay(500);     if (winner != 0) return;      var (aiX, aiY) = FindBestMove();     board[aiX, aiY] = 2;     if (CheckWin(aiX, aiY, 2))     {         winner = 2;         whiteScore++;     }     else     {         currentPlayer = 1;     }     StateHasChanged(); }  // 简单评分法:优先赢棋、阻挡对方、进攻 (int, int) FindBestMove() {     int maxScore = int.MinValue;     var bestMoves = new List<(int x, int y)>();     for (int x = 0; x < BoardSize; x++)     {         for (int y = 0; y < BoardSize; y++)         {             if (board[x, y] != 0) continue;             int score = EvaluatePoint(x, y, 2); // AI进攻             score = Math.Max(score, EvaluatePoint(x, y, 1)); // 防守             if (score > maxScore)             {                 maxScore = score;                 bestMoves.Clear();                 bestMoves.Add((x, y));             }             else if (score == maxScore)             {                 bestMoves.Add((x, y));             }         }     }     // 多个最佳点时随机选一个     return bestMoves[rand.Next(bestMoves.Count)]; }  // 评分函数:可根据实际需要调整权重 int EvaluatePoint(int x, int y, int player) {     int score = 0;     int opponent = player == 1 ? 2 : 1;     // 检查4连、3连、2连等     foreach (var dir in new[] { (1, 0), (0, 1), (1, 1), (1, -1) })     {         int count = 1 + CountDirection(x, y, dir.Item1, dir.Item2, player)                       + CountDirection(x, y, -dir.Item1, -dir.Item2, player);         if (count >= 5) score += 10000; // 直接获胜         else if (count == 4) score += 1000;         else if (count == 3) score += 100;         else if (count == 2) score += 10;          // 防守分数         int oppCount = 1 + CountDirection(x, y, dir.Item1, dir.Item2, opponent)                          + CountDirection(x, y, -dir.Item1, -dir.Item2, opponent);         if (oppCount >= 5) score += 9000; // 必须防守         else if (oppCount == 4) score += 900;         else if (oppCount == 3) score += 90;         else if (oppCount == 2) score += 9;     }     return score; } 

集成方法:

  1. 替换你的 AITurnAsync 方法为上述实现。
  2. @code 区域添加 FindBestMoveEvaluatePoint 方法。
  3. 可根据实际效果调整评分权重。

效果说明:

  • AI 会优先选择能赢的位置,其次堵住玩家的四连,再考虑进攻和防守三连、二连。
  • 这种评分法简单高效,能显著提升 AI 难度。
  • 若需更高难度,可进一步实现“活三”、“活四”识别、双向评分、甚至 Minimax 算法。

使用 Copilot AI + Blazor 编一个五子棋游戏

9. 添加难度切换按钮

添加三个难度选择按钮以及切换人机按钮

下面是实现“三个难度选择按钮(简单、中等、困难)”和“人机/人人切换按钮”的完整方法。
难度通过调整 AI 的评分权重实现,人机切换通过 isHumanVsAI 控制。请直接替换你的 Home.razor 文件内容:

@page "/"  <PageTitle>五子棋</PageTitle>  <h1>五子棋游戏</h1>  <div style="margin-bottom:10px;">     <b>难度:</b>     <button class="btn btn-sm @(aiLevel==0?"btn-primary":"btn-outline-primary")" @onclick="() => SetAILevel(0)">简单</button>     <button class="btn btn-sm @(aiLevel==1?"btn-primary":"btn-outline-primary")" @onclick="() => SetAILevel(1)">中等</button>     <button class="btn btn-sm @(aiLevel==2?"btn-primary":"btn-outline-primary")" @onclick="() => SetAILevel(2)">困难</button>     <span style="margin-left:20px;">         <b>模式:</b>         <button class="btn btn-sm @(isHumanVsAI?"btn-success":"btn-outline-success")" @onclick="ToggleMode">             @(isHumanVsAI ? "人机对战" : "人人对战")         </button>     </span> </div>  <div style="margin-bottom:10px;">     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))</b>     <span style="margin-left:20px;">黑子得分: <b>@blackScore</b></span>     <span style="margin-left:20px;">白子得分: <b>@whiteScore</b></span>     <button class="btn btn-danger" style="margin-left:20px;" @onclick="ResetScore">重置得分</button>     @if (winner != 0)     {         <span style="color:red;margin-left:20px;">胜者: @(winner == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))</span>         <button class="btn btn-primary" style="margin-left:20px;" @onclick="Restart">重新开始</button>     } </div>  <table style="border-collapse:collapse;">     @for (int y = 0; y < BoardSize; y++)     {         <tr>             @for (int x = 0; x < BoardSize; x++)             {                 int _x = x;                 int _y = y;                 <td style="width:32px;height:32px;border:1px solid #888;text-align:center;vertical-align:middle;cursor:pointer;background:@GetCellBg(x, y);"                     @onclick="@(() => PlacePiece(_x, _y))">                     @if (board[x, y] == 1)                     {                         <span style="font-size:24px;color:black;">●</span>                     }                     else if (board[x, y] == 2)                     {                         <span style="font-size:24px;color:#eee;text-shadow:0 0 2px #888;">●</span>                     }                 </td>             }         </tr>     } </table>  @code {     const int BoardSize = 15;     int[,] board = new int[BoardSize, BoardSize];     int currentPlayer = 1; // 1: 黑子, 2: 白子(电脑/玩家)     int winner = 0;     int blackScore = 0;     int whiteScore = 0;     Random rand = new();     bool isHumanVsAI = true;     int aiLevel = 1; // 0:简单 1:中等 2:困难      void PlacePiece(int x, int y)     {         if (winner != 0 || board[x, y] != 0)             return;          // 人机模式下,只有当前为玩家时可落子         if (isHumanVsAI && currentPlayer == 2)             return;          board[x, y] = currentPlayer;         if (CheckWin(x, y, currentPlayer))         {             winner = currentPlayer;             if (winner == 1)                 blackScore++;             else if (winner == 2)                 whiteScore++;         }         else         {             currentPlayer = 3 - currentPlayer;             if (isHumanVsAI && currentPlayer == 2 && winner == 0)             {                 StateHasChanged();                 _ = AITurnAsync();             }         }     }      async Task AITurnAsync()     {         await Task.Delay(500);         if (winner != 0) return;          var (aiX, aiY) = FindBestMove();         board[aiX, aiY] = 2;         if (CheckWin(aiX, aiY, 2))         {             winner = 2;             whiteScore++;         }         else         {             currentPlayer = 1;         }         StateHasChanged();     }      (int, int) FindBestMove()     {         if (aiLevel == 0)         {             // 简单:随机落子             var empty = new List<(int x, int y)>();             for (int i = 0; i < BoardSize; i++)                 for (int j = 0; j < BoardSize; j++)                     if (board[i, j] == 0)                         empty.Add((i, j));             return empty[rand.Next(empty.Count)];         }         else         {             // 中等/困难:评分法             int maxScore = int.MinValue;             var bestMoves = new List<(int x, int y)>();             for (int x = 0; x < BoardSize; x++)             {                 for (int y = 0; y < BoardSize; y++)                 {                     if (board[x, y] != 0) continue;                     int score = EvaluatePoint(x, y, 2);                     score = Math.Max(score, EvaluatePoint(x, y, 1));                     if (score > maxScore)                     {                         maxScore = score;                         bestMoves.Clear();                         bestMoves.Add((x, y));                     }                     else if (score == maxScore)                     {                         bestMoves.Add((x, y));                     }                 }             }             return bestMoves[rand.Next(bestMoves.Count)];         }     }      int EvaluatePoint(int x, int y, int player)     {         int score = 0;         int opponent = player == 1 ? 2 : 1;         // 不同难度权重         int[] winWeight = { 100, 10000, 10000 };         int[] fourWeight = { 10, 1000, 3000 };         int[] threeWeight = { 5, 100, 500 };         int[] twoWeight = { 2, 10, 50 };         int[] blockWinWeight = { 90, 9000, 9000 };         int[] blockFourWeight = { 9, 900, 2500 };         int[] blockThreeWeight = { 4, 90, 400 };         int[] blockTwoWeight = { 1, 9, 40 };          foreach (var dir in new[] { (1, 0), (0, 1), (1, 1), (1, -1) })         {             int count = 1 + CountDirection(x, y, dir.Item1, dir.Item2, player)                           + CountDirection(x, y, -dir.Item1, -dir.Item2, player);             if (count >= 5) score += winWeight[aiLevel];             else if (count == 4) score += fourWeight[aiLevel];             else if (count == 3) score += threeWeight[aiLevel];             else if (count == 2) score += twoWeight[aiLevel];              int oppCount = 1 + CountDirection(x, y, dir.Item1, dir.Item2, opponent)                              + CountDirection(x, y, -dir.Item1, -dir.Item2, opponent);             if (oppCount >= 5) score += blockWinWeight[aiLevel];             else if (oppCount == 4) score += blockFourWeight[aiLevel];             else if (oppCount == 3) score += blockThreeWeight[aiLevel];             else if (oppCount == 2) score += blockTwoWeight[aiLevel];         }         return score;     }      bool CheckWin(int x, int y, int player)     {         int[][] directions = new int[][]         {             new int[]{1,0}, new int[]{0,1}, new int[]{1,1}, new int[]{1,-1}         };         foreach (var dir in directions)         {             int count = 1;             count += CountDirection(x, y, dir[0], dir[1], player);             count += CountDirection(x, y, -dir[0], -dir[1], player);             if (count >= 5)                 return true;         }         return false;     }      int CountDirection(int x, int y, int dx, int dy, int player)     {         int count = 0;         for (int step = 1; step < 5; step++)         {             int nx = x + dx * step;             int ny = y + dy * step;             if (nx < 0 || nx >= BoardSize || ny < 0 || ny >= BoardSize)                 break;             if (board[nx, ny] == player)                 count++;             else                 break;         }         return count;     }      void Restart()     {         board = new int[BoardSize, BoardSize];         currentPlayer = 1;         winner = 0;         if (isHumanVsAI && currentPlayer == 2)         {             _ = AITurnAsync();         }     }      void ResetScore()     {         blackScore = 0;         whiteScore = 0;     }      void SetAILevel(int level)     {         aiLevel = level;         Restart();     }      void ToggleMode()     {         isHumanVsAI = !isHumanVsAI;         Restart();     }      string GetCellBg(int x, int y)     {         return (x + y) % 2 == 0 ? "#f9d77e" : "#eac066";     } } 

说明:

  • “简单”AI为随机落子,“中等/困难”AI为评分法,困难权重更高更难。
  • “人机对战”时白子为AI,“人人对战”时双方均为玩家。
  • 切换难度或模式会自动重开棋局。
  • 你可以根据需要调整权重数组以微调难度。

10. 最后贴上完整代码

@page "/"  <PageTitle>五子棋</PageTitle>  <h1>五子棋游戏</h1>  <div style="margin-bottom:10px;">     <b>难度:</b>     <button class="btn btn-sm @(aiLevel==0?"btn-primary":"btn-outline-primary")" @onclick="() => SetAILevel(0)">简单</button>     <button class="btn btn-sm @(aiLevel==1?"btn-primary":"btn-outline-primary")" @onclick="() => SetAILevel(1)">中等</button>     <button class="btn btn-sm @(aiLevel==2?"btn-primary":"btn-outline-primary")" @onclick="() => SetAILevel(2)">困难</button>     <span style="margin-left:20px;">         <b>模式:</b>         <button class="btn btn-sm @(isHumanVsAI?"btn-success":"btn-outline-success")" @onclick="ToggleMode">             @(isHumanVsAI ? "人机对战" : "人人对战")         </button>     </span> </div>  <div style="margin-bottom:10px;">     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))</b>     <span style="margin-left:20px;">黑子得分: <b>@blackScore</b></span>     <span style="margin-left:20px;">白子得分: <b>@whiteScore</b></span>     <button class="btn btn-danger" style="margin-left:20px;" @onclick="ResetScore">重置得分</button>     @if (winner != 0)     {         <span style="color:red;margin-left:20px;">胜者: @(winner == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))</span>         <button class="btn btn-primary" style="margin-left:20px;" @onclick="Restart">重新开始</button>     } </div>  <table style="border-collapse:collapse;">     @for (int y = 0; y < BoardSize; y++)     {         <tr>             @for (int x = 0; x < BoardSize; x++)             {                 int _x = x;                 int _y = y;                 <td style="width:32px;height:32px;border:1px solid #888;text-align:center;vertical-align:middle;cursor:pointer;background:@GetCellBg(x, y);"                     @onclick="@(() => PlacePiece(_x, _y))">                     @if (board[x, y] == 1)                     {                         <span style="font-size:24px;color:black;">●</span>                     }                     else if (board[x, y] == 2)                     {                         <span style="font-size:24px;color:#eee;text-shadow:0 0 2px #888;">●</span>                     }                 </td>             }         </tr>     } </table>  @code {     const int BoardSize = 15;     int[,] board = new int[BoardSize, BoardSize];     int currentPlayer = 1; // 1: 黑子, 2: 白子(电脑/玩家)     int winner = 0;     int blackScore = 0;     int whiteScore = 0;     Random rand = new();     bool isHumanVsAI = true;     int aiLevel = 1; // 0:简单 1:中等 2:困难      void PlacePiece(int x, int y)     {         if (winner != 0 || board[x, y] != 0)             return;          // 人机模式下,只有当前为玩家时可落子         if (isHumanVsAI && currentPlayer == 2)             return;          board[x, y] = currentPlayer;         if (CheckWin(x, y, currentPlayer))         {             winner = currentPlayer;             if (winner == 1)                 blackScore++;             else if (winner == 2)                 whiteScore++;         }         else         {             currentPlayer = 3 - currentPlayer;             if (isHumanVsAI && currentPlayer == 2 && winner == 0)             {                 StateHasChanged();                 _ = AITurnAsync();             }         }     }      async Task AITurnAsync()     {         await Task.Delay(500);         if (winner != 0) return;          var (aiX, aiY) = FindBestMove();         board[aiX, aiY] = 2;         if (CheckWin(aiX, aiY, 2))         {             winner = 2;             whiteScore++;         }         else         {             currentPlayer = 1;         }         StateHasChanged();     }      (int, int) FindBestMove()     {         if (aiLevel == 0)         {             // 简单:随机落子             var empty = new List<(int x, int y)>();             for (int i = 0; i < BoardSize; i++)                 for (int j = 0; j < BoardSize; j++)                     if (board[i, j] == 0)                         empty.Add((i, j));             return empty[rand.Next(empty.Count)];         }         else         {             // 中等/困难:评分法             int maxScore = int.MinValue;             var bestMoves = new List<(int x, int y)>();             for (int x = 0; x < BoardSize; x++)             {                 for (int y = 0; y < BoardSize; y++)                 {                     if (board[x, y] != 0) continue;                     int score = EvaluatePoint(x, y, 2);                     score = Math.Max(score, EvaluatePoint(x, y, 1));                     if (score > maxScore)                     {                         maxScore = score;                         bestMoves.Clear();                         bestMoves.Add((x, y));                     }                     else if (score == maxScore)                     {                         bestMoves.Add((x, y));                     }                 }             }             return bestMoves[rand.Next(bestMoves.Count)];         }     }      int EvaluatePoint(int x, int y, int player)     {         int score = 0;         int opponent = player == 1 ? 2 : 1;         // 不同难度权重         int[] winWeight = { 100, 10000, 10000 };         int[] fourWeight = { 10, 1000, 3000 };         int[] threeWeight = { 5, 100, 500 };         int[] twoWeight = { 2, 10, 50 };         int[] blockWinWeight = { 90, 9000, 9000 };         int[] blockFourWeight = { 9, 900, 2500 };         int[] blockThreeWeight = { 4, 90, 400 };         int[] blockTwoWeight = { 1, 9, 40 };          foreach (var dir in new[] { (1, 0), (0, 1), (1, 1), (1, -1) })         {             int count = 1 + CountDirection(x, y, dir.Item1, dir.Item2, player)                           + CountDirection(x, y, -dir.Item1, -dir.Item2, player);             if (count >= 5) score += winWeight[aiLevel];             else if (count == 4) score += fourWeight[aiLevel];             else if (count == 3) score += threeWeight[aiLevel];             else if (count == 2) score += twoWeight[aiLevel];              int oppCount = 1 + CountDirection(x, y, dir.Item1, dir.Item2, opponent)                              + CountDirection(x, y, -dir.Item1, -dir.Item2, opponent);             if (oppCount >= 5) score += blockWinWeight[aiLevel];             else if (oppCount == 4) score += blockFourWeight[aiLevel];             else if (oppCount == 3) score += blockThreeWeight[aiLevel];             else if (oppCount == 2) score += blockTwoWeight[aiLevel];         }         return score;     }      bool CheckWin(int x, int y, int player)     {         int[][] directions = new int[][]         {             new int[]{1,0}, new int[]{0,1}, new int[]{1,1}, new int[]{1,-1}         };         foreach (var dir in directions)         {             int count = 1;             count += CountDirection(x, y, dir[0], dir[1], player);             count += CountDirection(x, y, -dir[0], -dir[1], player);             if (count >= 5)                 return true;         }         return false;     }      int CountDirection(int x, int y, int dx, int dy, int player)     {         int count = 0;         for (int step = 1; step < 5; step++)         {             int nx = x + dx * step;             int ny = y + dy * step;             if (nx < 0 || nx >= BoardSize || ny < 0 || ny >= BoardSize)                 break;             if (board[nx, ny] == player)                 count++;             else                 break;         }         return count;     }      void Restart()     {         board = new int[BoardSize, BoardSize];         currentPlayer = 1;         winner = 0;         if (isHumanVsAI && currentPlayer == 2)         {             _ = AITurnAsync();         }     }      void ResetScore()     {         blackScore = 0;         whiteScore = 0;     }      void SetAILevel(int level)     {         aiLevel = level;         Restart();     }      void ToggleMode()     {         isHumanVsAI = !isHumanVsAI;         Restart();     }      string GetCellBg(int x, int y)     {         return (x + y) % 2 == 0 ? "#f9d77e" : "#eac066";     } } 

使用 Copilot AI + Blazor 编一个五子棋游戏

把运行模式改回来,Auto模式不占用服务器计算资源

打开 App.razor 编辑 两行 @rendermode="InteractiveServer" 改为 @rendermode="InteractiveAuto"

网友反馈

后续添加了美化UI, 添加动画, 悔棋

开源地址

源码g01Gobang

demo
https://game.app1.es 用blazor auto发布的

发表评论

评论已关闭。

相关文章