找回密码
 立即注册
首页 业界区 业界 Github Copilot 实战: 使用 Copilot AI + Blazor 编一个 ...

Github Copilot 实战: 使用 Copilot AI + Blazor 编一个五子棋游戏

涂流如 5 天前
开源地址

源码g01Gobang
demo
https://game.app1.es  用blazor auto发布的
1. 创建 Blazor web工程,选Auto

1.png

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

使用 Blazor 编一个五子棋游戏
2.png

3.复制代码测试

为了方便调试, 我们先把运行模式由 InteractiveAuto 改为 InteractiveServer
打开 App.razor 编辑 两行 @rendermode="InteractiveAuto" 改为 @rendermode="InteractiveServer"
3.png

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

4.png

无脑使用AI修复一下
5.png

结果发现不是这个问题. 是逻辑代码 @ 问题. 原因是PlacePiece闭包捕获的 x、y 是当前循环的值
既然人工发现了问题, 我们继续提醒一下AI, 尝试修复
6.png

最后还是没修复成功, 手动处理一下
  1. @page "/"
  2. <PageTitle>五子棋</PageTitle>
  3. <h1>五子棋游戏</h1>
  4.     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : "白子")</b>
  5.     @if (winner != 0)
  6.     {
  7.         胜者: @(winner == 1 ? "黑子" : "白子")
  8.         <button   @onclick="Restart">重新开始</button>
  9.     }
  10. <table >
  11.     @for (int y = 0; y < BoardSize; y++)
  12.     {
  13.         <tr>
  14.             @for (int x = 0; x < BoardSize; x++)
  15.             {
  16.                 int _x = x;
  17.                 int _y = y;
  18.                 <td
  19.                     @onclick="@(() => PlacePiece(_x, _y))">
  20.                     @if (board[x, y] == 1)
  21.                     {
  22.                         ●
  23.                     }
  24.                     else if (board[x, y] == 2)
  25.                     {
  26.                         ●
  27.                     }
  28.                 </td>
  29.             }
  30.         </tr>
  31.     }
  32. </table>
  33. @code {
  34.     const int BoardSize = 15;
  35.     int[,] board = new int[BoardSize, BoardSize];
  36.     int currentPlayer = 1; // 1: 黑子, 2: 白子
  37.     int winner = 0;
  38.     void PlacePiece(int x, int y)
  39.     {
  40.         // 修复:增加边界检查,防止越界
  41.         if (x < 0 || x >= BoardSize || y < 0 || y >= BoardSize)
  42.             return;
  43.         if (winner != 0 || board[x, y] != 0)
  44.             return;
  45.         board[x, y] = currentPlayer;
  46.         if (CheckWin(x, y, currentPlayer))
  47.         {
  48.             winner = currentPlayer;
  49.         }
  50.         else
  51.         {
  52.             currentPlayer = 3 - currentPlayer; // 切换玩家
  53.         }
  54.     }
  55.     bool CheckWin(int x, int y, int player)
  56.     {
  57.         int[][] directions = new int[][]
  58.         {
  59.             new int[]{1,0}, // 横
  60.             new int[]{0,1}, // 竖
  61.             new int[]{1,1}, // 斜
  62.             new int[]{1,-1} // 反斜
  63.         };
  64.         foreach (var dir in directions)
  65.         {
  66.             int count = 1;
  67.             count += CountDirection(x, y, dir[0], dir[1], player);
  68.             count += CountDirection(x, y, -dir[0], -dir[1], player);
  69.             if (count >= 5)
  70.                 return true;
  71.         }
  72.         return false;
  73.     }
  74.     int CountDirection(int x, int y, int dx, int dy, int player)
  75.     {
  76.         int count = 0;
  77.         for (int step = 1; step < 5; step++)
  78.         {
  79.             int nx = x + dx * step;
  80.             int ny = y + dy * step;
  81.             if (nx < 0 || nx >= BoardSize || ny < 0 || ny >= BoardSize)
  82.                 break;
  83.             if (board[nx, ny] == player)
  84.                 count++;
  85.             else
  86.                 break;
  87.         }
  88.         return count;
  89.     }
  90.     void Restart()
  91.     {
  92.         board = new int[BoardSize, BoardSize];
  93.         currentPlayer = 1;
  94.         winner = 0;
  95.     }
  96.     string GetCellBg(int x, int y)
  97.     {
  98.         return (x + y) % 2 == 0 ? "#f9d77e" : "#eac066";
  99.     }
  100. }
复制代码
说明:

  • 添加 int _x = x; int _y = y; 确保闭包捕获的 _x、_y 是当前循环的值,避免 Blazor 渲染时闭包变量引用错误。
  • 在 PlacePiece 方法开头增加边界检查,防止非法坐标导致数组越界异常。
这样可以彻底修复 PlacePiece 拿到的变量不正确和越界的问题。
5. 运行效果

7.png

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

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

  • 在 @code 区域添加分数字段:
  1. int blackScore = 0;
  2. int whiteScore = 0;
复制代码

  • 在玩家获胜时更新分数:
  1. void PlacePiece(int x, int y)
  2. {
  3.     if (x < 0 || x >= BoardSize || y < 0 || y >= BoardSize)
  4.         return;
  5.     if (winner != 0 || board[x, y] != 0)
  6.         return;
  7.     board[x, y] = currentPlayer;
  8.     if (CheckWin(x, y, currentPlayer))
  9.     {
  10.         winner = currentPlayer;
  11.         if (winner == 1)
  12.             blackScore++;
  13.         else if (winner == 2)
  14.             whiteScore++;
  15.     }
  16.     else
  17.     {
  18.         currentPlayer = 3 - currentPlayer; // 切换玩家
  19.     }
  20. }
复制代码

  • 在页面上显示分数(比如在标题下方):
  1.     当前玩家: <b>@(currentPlayer == 1 ? "黑子" : "白子")</b>
  2.     黑子得分: <b>@blackScore</b>
  3.     白子得分: <b>@whiteScore</b>
  4.     @if (winner != 0)
  5.     {
  6.         胜者: @(winner == 1 ? "黑子" : "白子")
  7.         <button   @onclick="Restart">重新开始</button>
  8.     }
复制代码
这样每次有玩家获胜时,分数都会自动更新并显示在页面上。
8.png

7. 加入<button   @onclick="ResetScore">重置得分</button>功能

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

  • 添加<button   @onclick="ResetScore">重置得分</button>方法:
  1. void ResetScore()
  2. {
  3.     blackScore = 0;
  4.     whiteScore = 0;
  5. }
复制代码

  • 在页面上添加按钮:
  1. <button   @onclick="ResetScore">重置得分</button>
复制代码
建议将此按钮放在分数显示附近,例如:
  1.     当前玩家: [b]@(currentPlayer == 1 ? "黑子" : "白子")[/b]    黑子得分: [b]@blackScore[/b]    白子得分: [b]@whiteScore[/b]    <button   @onclick="ResetScore">重置得分</button>    @if (winner != 0)    {        胜者: @(winner == 1 ? "黑子" : "白子")        重新开始    }
复制代码
这样点击“<button   @onclick="ResetScore">重置得分</button>”按钮即可将分数清零。
8. 在这个五子棋游戏中添加 AI 对手功能

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

  • 判断当前玩家是否为 AI:如 currentPlayer == 2 时,自动调用 AI 落子逻辑。
  • 实现 AI 落子算法:可以先用简单的随机落子,后续可扩展为更智能的算法。
  • 在玩家落子后自动触发 AI 落子:用 StateHasChanged() 刷新界面,必要时用 await Task.Delay() 增加落子延迟。
下面是具体实现方法(以白子为 AI):
  1. @page "/"五子棋[size=6]五子棋游戏[/size]
  2.     当前玩家: [b]@(currentPlayer == 1 ? "黑子" : "白子(电脑)")[/b]    黑子得分: [b]@blackScore[/b]    白子得分: [b]@whiteScore[/b]    <button   @onclick="ResetScore">重置得分</button>    @if (winner != 0)    {        胜者: @(winner == 1 ? "黑子" : "白子(电脑)")        重新开始    }[table]    @for (int y = 0; y < BoardSize; y++)    {        [tr]            @for (int x = 0; x < BoardSize; x++)            {                int _x = x;                int _y = y;                [td]                    @if (board[x, y] == 1)                    {                        ●                    }                    else if (board[x, y] == 2)                    {                        ●                    }                [/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();        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 并在 OnInitialized 或 Restart 时调用 AITurnAsync()。
9.png

用是能用了, 就是这个AI太傻了,继续优化一下
要优化五子棋 AI 的落子策略,可以采用“优先级评分法”来提升难度。基本思路如下:

  • 优先考虑能直接获胜的位置(如 AI 连成五子)。
  • 阻止玩家获胜(如玩家有四连,AI 必须堵住)。
  • 优先进攻(如自己有三连、二连)。
  • 其次防守(如玩家有三连、二连)。
  • 否则随机或选择评分最高的位置
下面是一个简单的评分法实现,AI 会优先选择能赢、能防守、能进攻的位置:
  1. async Task AITurnAsync()
  2. {
  3.     await Task.Delay(500);
  4.     if (winner != 0) return;
  5.     var (aiX, aiY) = FindBestMove();
  6.     board[aiX, aiY] = 2;
  7.     if (CheckWin(aiX, aiY, 2))
  8.     {
  9.         winner = 2;
  10.         whiteScore++;
  11.     }
  12.     else
  13.     {
  14.         currentPlayer = 1;
  15.     }
  16.     StateHasChanged();
  17. }
  18. // 简单评分法:优先赢棋、阻挡对方、进攻
  19. (int, int) FindBestMove()
  20. {
  21.     int maxScore = int.MinValue;
  22.     var bestMoves = new List<(int x, int y)>();
  23.     for (int x = 0; x < BoardSize; x++)
  24.     {
  25.         for (int y = 0; y < BoardSize; y++)
  26.         {
  27.             if (board[x, y] != 0) continue;
  28.             int score = EvaluatePoint(x, y, 2); // AI进攻
  29.             score = Math.Max(score, EvaluatePoint(x, y, 1)); // 防守
  30.             if (score > maxScore)
  31.             {
  32.                 maxScore = score;
  33.                 bestMoves.Clear();
  34.                 bestMoves.Add((x, y));
  35.             }
  36.             else if (score == maxScore)
  37.             {
  38.                 bestMoves.Add((x, y));
  39.             }
  40.         }
  41.     }
  42.     // 多个最佳点时随机选一个
  43.     return bestMoves[rand.Next(bestMoves.Count)];
  44. }
  45. // 评分函数:可根据实际需要调整权重
  46. int EvaluatePoint(int x, int y, int player)
  47. {
  48.     int score = 0;
  49.     int opponent = player == 1 ? 2 : 1;
  50.     // 检查4连、3连、2连等
  51.     foreach (var dir in new[] { (1, 0), (0, 1), (1, 1), (1, -1) })
  52.     {
  53.         int count = 1 + CountDirection(x, y, dir.Item1, dir.Item2, player)
  54.                       + CountDirection(x, y, -dir.Item1, -dir.Item2, player);
  55.         if (count >= 5) score += 10000; // 直接获胜
  56.         else if (count == 4) score += 1000;
  57.         else if (count == 3) score += 100;
  58.         else if (count == 2) score += 10;
  59.         // 防守分数
  60.         int oppCount = 1 + CountDirection(x, y, dir.Item1, dir.Item2, opponent)
  61.                          + CountDirection(x, y, -dir.Item1, -dir.Item2, opponent);
  62.         if (oppCount >= 5) score += 9000; // 必须防守
  63.         else if (oppCount == 4) score += 900;
  64.         else if (oppCount == 3) score += 90;
  65.         else if (oppCount == 2) score += 9;
  66.     }
  67.     return score;
  68. }
复制代码
集成方法:

  • 替换你的 AITurnAsync 方法为上述实现。
  • 在 @code 区域添加 FindBestMove 和 EvaluatePoint 方法。
  • 可根据实际效果调整评分权重。
效果说明:

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

9. 添加难度切换按钮

添加三个难度选择按钮以及切换人机按钮
下面是实现“三个难度选择按钮(简单、中等、困难)”和“人机/人人切换按钮”的完整方法。
难度通过调整 AI 的评分权重实现,人机切换通过 isHumanVsAI 控制。请直接替换你的 Home.razor 文件内容:
  1. @page "/"五子棋[size=6]五子棋游戏[/size]
  2.     [b]难度:[/b]    简单    中等    困难            [b]模式:[/b]                    @(isHumanVsAI ? "人机对战" : "人人对战")                当前玩家: [b]@(currentPlayer == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))[/b]    黑子得分: [b]@blackScore[/b]    白子得分: [b]@whiteScore[/b]    <button   @onclick="ResetScore">重置得分</button>    @if (winner != 0)    {        胜者: @(winner == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))        重新开始    }[table]    @for (int y = 0; y < BoardSize; y++)    {        [tr]            @for (int x = 0; x < BoardSize; x++)            {                int _x = x;                int _y = y;                [td]                    @if (board[x, y] == 1)                    {                        ●                    }                    else if (board[x, y] == 2)                    {                        ●                    }                [/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();            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();            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. 最后贴上完整代码
  1. @page "/"五子棋[size=6]五子棋游戏[/size]
  2.     [b]难度:[/b]    简单    中等    困难            [b]模式:[/b]                    @(isHumanVsAI ? "人机对战" : "人人对战")                当前玩家: [b]@(currentPlayer == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))[/b]    黑子得分: [b]@blackScore[/b]    白子得分: [b]@whiteScore[/b]    <button   @onclick="ResetScore">重置得分</button>    @if (winner != 0)    {        胜者: @(winner == 1 ? "黑子" : (isHumanVsAI ? "白子(电脑)" : "白子"))        重新开始    }[table]    @for (int y = 0; y < BoardSize; y++)    {        [tr]            @for (int x = 0; x < BoardSize; x++)            {                int _x = x;                int _y = y;                [td]                    @if (board[x, y] == 1)                    {                        ●                    }                    else if (board[x, y] == 2)                    {                        ●                    }                [/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();            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();            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";    }}
复制代码
11.png

把运行模式改回来,Auto模式不占用服务器计算资源
打开 App.razor 编辑 两行 @rendermode="InteractiveServer" 改为 @rendermode="InteractiveAuto"
网友反馈

后续添加了美化UI, 添加动画, 悔棋
开源地址

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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册