c# winform 五子棋 人机对战 (详细)

目录

1.前言

2.人机对战主要功能实现

3.其他功能修改

4.完整代码


1.前言

c# winform 简单五子棋,支持连续悔棋。-CSDN博客

基础版跳链接。建议先阅读。

 在基础版的界面上增加两个groupBox,并各自放两个radioButton。

c# winform 五子棋 人机对战 (详细)_第1张图片

c# winform 五子棋 人机对战 (详细)_第2张图片c# winform 五子棋 人机对战 (详细)_第3张图片

在基础版上form1.cs中增加变量
        private bool isAIThinking = false;//判断是否该ai走
        private Timer aiDelayTimer; //延时

增加

            // 初始化AI延迟计时器
            aiDelayTimer = new Timer();
            aiDelayTimer.Interval = 500;
            aiDelayTimer.Tick += AiDelayTimer_Tick;

            // 设置默认游戏模式
            radioButton_Human.Checked = true;
            radioButton_PlayerFirst.Checked = true;

            // 添加事件处理
            radioButton_Human.CheckedChanged += GameMode_CheckedChanged;
            radioButton_AI.CheckedChanged += GameMode_CheckedChanged;

c# winform 五子棋 人机对战 (详细)_第4张图片

其中,GameMode_CheckedChanged:

当用户点击 radioButton_Human(人人对战)或 radioButton_AI(人机对战)时,会触发 CheckedChanged 事件。

        private void GameMode_CheckedChanged(object sender, EventArgs e)// 游戏模式切换事件
        {
            groupBox2.Enabled = radioButton_AI.Checked; // 只有AI模式才启用先手选择
        }

AiDelayTimer_Tick:

添加延迟效果避免界面突然跳变;通过 isAIThinking 标志位确保AI在思考期间不会重复执行落子逻辑。

        private void AiDelayTimer_Tick(object sender, EventArgs e)
        {
            aiDelayTimer.Stop();
            if (!isAIThinking && !chesscheck && radioButton_AI.Checked && start)
            {
                isAIThinking = true;
                AIMove();
                isAIThinking = false;
            }
        }


 

form1.designer.cs完整代码如下:

namespace wuziqi2
{
    partial class Form1
    {
        /// 
        /// 必需的设计器变量。
        /// 
        private System.ComponentModel.IContainer components = null;

        /// 
        /// 清理所有正在使用的资源。
        /// 
        /// 如果应释放托管资源,为 true;否则为 false。
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// 
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// 
        private void InitializeComponent()
        {
            this.picturebox_chessboard = new System.Windows.Forms.PictureBox();
            this.button_start = new System.Windows.Forms.Button();
            this.button_revoke = new System.Windows.Forms.Button();
            this.radioButton_Human = new System.Windows.Forms.RadioButton();
            this.radioButton_AI = new System.Windows.Forms.RadioButton();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.radioButton_PlayerFirst = new System.Windows.Forms.RadioButton();
            this.radioButton_AIFirst = new System.Windows.Forms.RadioButton();
            ((System.ComponentModel.ISupportInitialize)(this.picturebox_chessboard)).BeginInit();
            this.groupBox1.SuspendLayout();
            this.groupBox2.SuspendLayout();
            this.SuspendLayout();
            // 
            // picturebox_chessboard
            // 
            this.picturebox_chessboard.BackColor = System.Drawing.SystemColors.GradientActiveCaption;
            this.picturebox_chessboard.Location = new System.Drawing.Point(52, 51);
            this.picturebox_chessboard.Name = "picturebox_chessboard";
            this.picturebox_chessboard.Size = new System.Drawing.Size(875, 875);
            this.picturebox_chessboard.TabIndex = 0;
            this.picturebox_chessboard.TabStop = false;
            this.picturebox_chessboard.Click += new System.EventHandler(this.picturebox_chessboard_Click);
            // 
            // button_start
            // 
            this.button_start.Location = new System.Drawing.Point(994, 69);
            this.button_start.Name = "button_start";
            this.button_start.Size = new System.Drawing.Size(165, 91);
            this.button_start.TabIndex = 1;
            this.button_start.Text = "开始游戏";
            this.button_start.UseVisualStyleBackColor = true;
            this.button_start.Click += new System.EventHandler(this.button_start_Click);
            // 
            // button_revoke
            // 
            this.button_revoke.Location = new System.Drawing.Point(1215, 69);
            this.button_revoke.Name = "button_revoke";
            this.button_revoke.Size = new System.Drawing.Size(165, 91);
            this.button_revoke.TabIndex = 3;
            this.button_revoke.Text = "悔棋";
            this.button_revoke.UseVisualStyleBackColor = true;
            this.button_revoke.Click += new System.EventHandler(this.button_revoke_Click);
            // 
            // radioButton_Human
            // 
            this.radioButton_Human.AutoSize = true;
            this.radioButton_Human.Location = new System.Drawing.Point(11, 124);
            this.radioButton_Human.Name = "radioButton_Human";
            this.radioButton_Human.Size = new System.Drawing.Size(105, 22);
            this.radioButton_Human.TabIndex = 4;
            this.radioButton_Human.TabStop = true;
            this.radioButton_Human.Text = "人人对战";
            this.radioButton_Human.UseVisualStyleBackColor = true;
            // 
            // radioButton_AI
            // 
            this.radioButton_AI.AutoSize = true;
            this.radioButton_AI.Location = new System.Drawing.Point(11, 180);
            this.radioButton_AI.Name = "radioButton_AI";
            this.radioButton_AI.Size = new System.Drawing.Size(105, 22);
            this.radioButton_AI.TabIndex = 5;
            this.radioButton_AI.TabStop = true;
            this.radioButton_AI.Text = "人机对战";
            this.radioButton_AI.UseVisualStyleBackColor = true;
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.radioButton_AI);
            this.groupBox1.Controls.Add(this.radioButton_Human);
            this.groupBox1.Location = new System.Drawing.Point(978, 219);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(181, 367);
            this.groupBox1.TabIndex = 6;
            this.groupBox1.TabStop = false;
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.Add(this.radioButton_AIFirst);
            this.groupBox2.Controls.Add(this.radioButton_PlayerFirst);
            this.groupBox2.Location = new System.Drawing.Point(1235, 229);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Size = new System.Drawing.Size(187, 366);
            this.groupBox2.TabIndex = 7;
            this.groupBox2.TabStop = false;
            // 
            // radioButton_PlayerFirst
            // 
            this.radioButton_PlayerFirst.AutoSize = true;
            this.radioButton_PlayerFirst.Location = new System.Drawing.Point(6, 124);
            this.radioButton_PlayerFirst.Name = "radioButton_PlayerFirst";
            this.radioButton_PlayerFirst.Size = new System.Drawing.Size(105, 22);
            this.radioButton_PlayerFirst.TabIndex = 0;
            this.radioButton_PlayerFirst.TabStop = true;
            this.radioButton_PlayerFirst.Text = "玩家先手";
            this.radioButton_PlayerFirst.UseVisualStyleBackColor = true;
            // 
            // radioButton_AIFirst
            // 
            this.radioButton_AIFirst.AutoSize = true;
            this.radioButton_AIFirst.Location = new System.Drawing.Point(6, 180);
            this.radioButton_AIFirst.Name = "radioButton_AIFirst";
            this.radioButton_AIFirst.Size = new System.Drawing.Size(87, 22);
            this.radioButton_AIFirst.TabIndex = 1;
            this.radioButton_AIFirst.TabStop = true;
            this.radioButton_AIFirst.Text = "AI先手";
            this.radioButton_AIFirst.UseVisualStyleBackColor = true;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(1434, 993);
            this.Controls.Add(this.groupBox2);
            this.Controls.Add(this.groupBox1);
            this.Controls.Add(this.button_revoke);
            this.Controls.Add(this.button_start);
            this.Controls.Add(this.picturebox_chessboard);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.picturebox_chessboard)).EndInit();
            this.groupBox1.ResumeLayout(false);
            this.groupBox1.PerformLayout();
            this.groupBox2.ResumeLayout(false);
            this.groupBox2.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.PictureBox picturebox_chessboard;
        private System.Windows.Forms.Button button_start;
        private System.Windows.Forms.Button button_revoke;
        private System.Windows.Forms.RadioButton radioButton_Human;
        private System.Windows.Forms.RadioButton radioButton_AI;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.GroupBox groupBox2;
        private System.Windows.Forms.RadioButton radioButton_AIFirst;
        private System.Windows.Forms.RadioButton radioButton_PlayerFirst;
    }




}


2.人机对战主要功能实现

        private void AIMove()//ai落子
        {
            if (!start || chesscheck) return; // 不是AI回合或游戏未开始

            
            Point aiMove = GetBestMove();
            

            // 确保AI找到有效位置
            if (aiMove.X < 1 || aiMove.X > size_chessboard ||
                aiMove.Y < 1 || aiMove.Y > size_chessboard ||
                array_chess[aiMove.X, aiMove.Y] != 0)
            {
                aiMove = GetRandomMove(); //  fallback
            }

            // 在Bitmap上绘制AI的棋子
            using (Graphics g = Graphics.FromImage(chessBoardBitmap))
            {
                g.FillEllipse(Brushes.Black, aiMove.X * width - adjust_size, aiMove.Y * width - adjust_size, 26, 22);
                array_chess[aiMove.X, aiMove.Y] = -1;
                historical_records[num_check][0] = aiMove.X;
                historical_records[num_check][1] = aiMove.Y;
                historical_records[num_check][2] = -1;
            }

            num_check++;
            chesscheck = !chesscheck;
            picturebox_chessboard.Invalidate();
            judge_success(aiMove.X, aiMove.Y);
        }

        private Point GetRandomMove()//随便走
        {
            Random rand = new Random();
            int x, y;
            do
            {
                x = rand.Next(1, size_chessboard + 1);
                y = rand.Next(1, size_chessboard + 1);
            } while (array_chess[x, y] != 0);

            return new Point(x, y);
        }

        private Point GetBestMove()//好好走
        {
            // 这里实现一个简化的评估函数
            int bestScore = int.MinValue;
            Point bestMove = new Point();

            for (int x = 1; x <= size_chessboard; x++)
            {
                for (int y = 1; y <= size_chessboard; y++)
                {
                    if (array_chess[x, y] == 0)
                    {
                        // 模拟落子
                        array_chess[x, y] = -1;

                        // 评估这个位置的分数
                        int score = EvaluatePosition(x, y, -1);

                        // 检查是否立即获胜
                        if (CheckWinCondition(x, y, -1))
                        {
                            array_chess[x, y] = 0;
                            return new Point(x, y);
                        }

                        // 考虑对手的回应(简化版)
                        for (int x2 = 1; x2 <= size_chessboard; x2++)
                        {
                            for (int y2 = 1; y2 <= size_chessboard; y2++)
                            {
                                if (array_chess[x2, y2] == 0)
                                {
                                    array_chess[x2, y2] = 1;
                                    if (CheckWinCondition(x2, y2, 1))
                                    {
                                        score -= 50; // 如果对手能在这里获胜,降低分数
                                    }
                                    array_chess[x2, y2] = 0;
                                }
                            }
                        }

                        // 恢复
                        array_chess[x, y] = 0;

                        // 更新最佳落子
                        if (score > bestScore)
                        {
                            bestScore = score;
                            bestMove = new Point(x, y);
                        }
                    }
                }
            }

            // 如果所有位置分数相同,随机选择一个
            if (bestScore == int.MinValue)
            {
                return GetRandomMove();
            }

            return bestMove;
        }
     
        private bool CheckWinCondition(int x, int y, int player)// 辅助方法:检查是否满足胜利条件
        {
            // 定义四个方向
            int[][] directions = new int[][]
            {
        new int[] { 0, 1 },   // 水平
        new int[] { 1, 0 },   // 垂直
        new int[] { 1, 1 },   // 左上到右下
        new int[] { 1, -1 }   // 右上到左下
            };

            foreach (int[] dir in directions)
            {
                int count = 1; // 当前位置已经有一个棋子

                // 正向检查
                int dx = dir[0], dy = dir[1];
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x + i * dx;
                    int ny = y + i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard ||
                        array_chess[nx, ny] != player)
                        break;
                    count++;
                }

                // 反向检查
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x - i * dx;
                    int ny = y - i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard ||
                        array_chess[nx, ny] != player)
                        break;
                    count++;
                }

                if (count >= 5)
                    return true;
            }

            return false;
        }

        private int EvaluatePosition(int x, int y, int player)// 评估函数
        {
            int score = 0;

            // 1. 中心位置加分
            int center = size_chessboard / 2 + 1;
            int distanceToCenter = Math.Max(Math.Abs(x - center), Math.Abs(y - center));
            score += (center - distanceToCenter) * 2;

            // 2. 检查形成的连线
            int[][] directions = new int[][]
            {
        new int[] { 0, 1 }, new int[] { 1, 0 },
        new int[] { 1, 1 }, new int[] { 1, -1 }
            };

            foreach (int[] dir in directions)
            {
                int count = 1;
                int emptySides = 0;

                // 正向检查
                int dx = dir[0], dy = dir[1];
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x + i * dx;
                    int ny = y + i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard)
                        break;
                    if (array_chess[nx, ny] == player)
                        count++;
                    else if (array_chess[nx, ny] == 0)
                        emptySides++;
                    else
                        break;
                }

                // 反向检查
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x - i * dx;
                    int ny = y - i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard)
                        break;
                    if (array_chess[nx, ny] == player)
                        count++;
                    else if (array_chess[nx, ny] == 0)
                        emptySides++;
                    else
                        break;
                }

                // 根据连线长度和两端空位评分
                if (count >= 5)
                    score += 100000; // 已经赢了
                else if (count == 4 && emptySides == 2)
                    score += 10000;  // 活四
                else if (count == 4 && emptySides == 1)
                    score += 1000;   // 冲四
                else if (count == 3 && emptySides == 2)
                    score += 500;    // 活三
                else if (count == 3 && emptySides == 1)
                    score += 100;    // 眠三
                else if (count == 2 && emptySides == 2)
                    score += 50;     // 活二
                else if (count == 2 && emptySides == 1)
                    score += 10;     // 眠二
            }

            return score;
        }

GetBestMove():

遍历棋盘(1 到 size_chessboard),找到所有可以落子的空位(array_chess[x, y] == 0)。

  • 如果落子后直接获胜(CheckWinCondition 返回 true),则直接返回该位置。
  • 如果对手能获胜,则降低当前 AI 落子的得分(score -= 50)。
  • 如果所有位置得分相同(或无效),则随机选择一个位置(GetRandomMove())。

EvaluatePosition() 评估方法:

  • 遍历 4 个方向(水平、垂直、左斜、右斜),检查当前落子是否能形成 活四、冲四、活三、眠三、活二、眠二 等棋型。
  • 不同棋型赋予不同分数:
    • 活四count == 4 && emptySides == 2):10000 分(极高优先级)。
    • 冲四count == 4 && emptySides == 1):1000 分。
    • 活三count == 3 && emptySides == 2):500 分。
    • 眠三count == 3 && emptySides == 1):100 分。
    • 活二count == 2 && emptySides == 2):50 分。
    • 眠二count == 2 && emptySides == 1):10 分。

CheckWinCondition() 方法:和基础班的judge_success 类似。

3.其他功能修改

原版的picturebox_chessboard_Click只支持人落子

现在进行分离重构。

修改后代码如下:

        private void picturebox_chessboard_Click(object sender, EventArgs e)//点击棋盘
        {
            if (!start) return;

            // 人人对战模式
            if (radioButton_Human.Checked)
            {
                HumanMove();
                return;
            }

            // 人机对战模式且玩家回合
            if (radioButton_AI.Checked && chesscheck)
            {
                HumanMove();
            }

        }

        private void HumanMove()//人走
        {
            try
            {
                Point box1Ponit = picturebox_chessboard.PointToClient(Control.MousePosition);
                int x = box1Ponit.X % width > width / 2 ? box1Ponit.X / width + 1 : box1Ponit.X / width;
                int y = box1Ponit.Y % width > width / 2 ? box1Ponit.Y / width + 1 : box1Ponit.Y / width;

                // 边界检查
                if (x < 1 || x > size_chessboard || y < 1 || y > size_chessboard)
                    return;

                if (array_chess[x, y] != 0)
                {
                    MessageBox.Show("请勿重复落子");
                    return;
                }

                // 在Bitmap上绘制棋子
                using (Graphics g = Graphics.FromImage(chessBoardBitmap))
                {
                    if (chesscheck) // 白子回合
                    {
                        g.FillEllipse(Brushes.White, x * width - adjust_size, y * width - adjust_size, 26, 22);
                        array_chess[x, y] = 1;
                        historical_records[num_check][0] = x;
                        historical_records[num_check][1] = y;
                        historical_records[num_check][2] = 1;
                    }
                    else // 黑子回合
                    {
                        g.FillEllipse(Brushes.Black, x * width - adjust_size, y * width - adjust_size, 26, 22);
                        array_chess[x, y] = -1;
                        historical_records[num_check][0] = x;
                        historical_records[num_check][1] = y;
                        historical_records[num_check][2] = -1;
                    }
                }

                num_check++;
                chesscheck = !chesscheck;
                picturebox_chessboard.Invalidate();
                judge_success(x, y);

                // 如果是人机对战且轮到AI
                if (radioButton_AI.Checked && !chesscheck && start)
                {
                    aiDelayTimer.Stop();
                    aiDelayTimer.Start();
                }
            }
            catch
            {
                MessageBox.Show("落子错误");
            }
        }

修改initialize_chessboard()

private void initialize_chessboard()
{
    // 原有初始化代码...
    
    // 如果是人机对战模式,AI先手或玩家先手
    if (!start && radioButton_AI.Checked) // 假设你添加了一个RadioButton选择人机对战
    {
        start = true;
        if (radioButton_AIFirst.Checked) // AI先手的RadioButton
        {
            chesscheck = false; // 设置为黑子(AI)先手
            AIMove();
        }
        else
        {
            chesscheck = true; // 玩家先手(白子)
        }
    }
}

4.完整代码
 


form1.cs

using System;
using System.Drawing;
using System.Windows.Forms;


namespace wuziqi2
{
    public partial class Form1 : Form
    {

        public bool start = false;//记录是否开始   
        public bool chesscheck = true;//true 白子回合,false黑子回合
        public const int size_chessboard = 13;//棋盘几x几格
        public int[,] array_chess = new int[size_chessboard + 1, size_chessboard + 1];
        //棋盘二维数组(棋盘二维数组分为三个值,0代表此格未落子,1代表此格为红子,-1代表黑子。)
        public int width;//方格宽度
        int num_check = 0;//记录落子次数
        int[][] historical_records;//记录落子的坐标
        public int adjust_size = 3;//落子偏移量(如果棋子没有正好落在交叉点,调整此量)
        public Bitmap chessBoardBitmap; // 缓存棋盘和棋子的位图
        private bool isAIThinking = false;//判断是否该ai走
        private Timer aiDelayTimer; //延时

        public Form1()
        {
            InitializeComponent();

            // 初始化AI延迟计时器
            aiDelayTimer = new Timer();
            aiDelayTimer.Interval = 500;
            aiDelayTimer.Tick += AiDelayTimer_Tick;

            // 设置默认游戏模式
            radioButton_Human.Checked = true;
            radioButton_PlayerFirst.Checked = true;

            // 添加事件处理
            radioButton_Human.CheckedChanged += GameMode_CheckedChanged;
            radioButton_AI.CheckedChanged += GameMode_CheckedChanged;

        }
      
        private void GameMode_CheckedChanged(object sender, EventArgs e)// 游戏模式切换事件
        {
            groupBox2.Enabled = radioButton_AI.Checked; // 只有AI模式才启用先手选择
        }

        private void AIMove()//ai落子
        {
            if (!start || chesscheck) return; // 不是AI回合或游戏未开始

            
            Point aiMove = GetBestMove();
            

            // 确保AI找到有效位置
            if (aiMove.X < 1 || aiMove.X > size_chessboard ||
                aiMove.Y < 1 || aiMove.Y > size_chessboard ||
                array_chess[aiMove.X, aiMove.Y] != 0)
            {
                aiMove = GetRandomMove(); //  fallback
            }

            // 在Bitmap上绘制AI的棋子
            using (Graphics g = Graphics.FromImage(chessBoardBitmap))
            {
                g.FillEllipse(Brushes.Black, aiMove.X * width - adjust_size, aiMove.Y * width - adjust_size, 26, 22);
                array_chess[aiMove.X, aiMove.Y] = -1;
                historical_records[num_check][0] = aiMove.X;
                historical_records[num_check][1] = aiMove.Y;
                historical_records[num_check][2] = -1;
            }

            num_check++;
            chesscheck = !chesscheck;
            picturebox_chessboard.Invalidate();
            judge_success(aiMove.X, aiMove.Y);
        }

        private Point GetRandomMove()//随便走
        {
            Random rand = new Random();
            int x, y;
            do
            {
                x = rand.Next(1, size_chessboard + 1);
                y = rand.Next(1, size_chessboard + 1);
            } while (array_chess[x, y] != 0);

            return new Point(x, y);
        }

        private Point GetBestMove()//好好走
        {
            // 这里实现一个简化的评估函数
            int bestScore = int.MinValue;
            Point bestMove = new Point();

            for (int x = 1; x <= size_chessboard; x++)
            {
                for (int y = 1; y <= size_chessboard; y++)
                {
                    if (array_chess[x, y] == 0)
                    {
                        // 模拟落子
                        array_chess[x, y] = -1;

                        // 评估这个位置的分数
                        int score = EvaluatePosition(x, y, -1);

                        // 检查是否立即获胜
                        if (CheckWinCondition(x, y, -1))
                        {
                            array_chess[x, y] = 0;
                            return new Point(x, y);
                        }

                        // 考虑对手的回应(简化版)
                        for (int x2 = 1; x2 <= size_chessboard; x2++)
                        {
                            for (int y2 = 1; y2 <= size_chessboard; y2++)
                            {
                                if (array_chess[x2, y2] == 0)
                                {
                                    array_chess[x2, y2] = 1;
                                    if (CheckWinCondition(x2, y2, 1))
                                    {
                                        score -= 50; // 如果对手能在这里获胜,降低分数
                                    }
                                    array_chess[x2, y2] = 0;
                                }
                            }
                        }

                        // 恢复
                        array_chess[x, y] = 0;

                        // 更新最佳落子
                        if (score > bestScore)
                        {
                            bestScore = score;
                            bestMove = new Point(x, y);
                        }
                    }
                }
            }

            // 如果所有位置分数相同,随机选择一个
            if (bestScore == int.MinValue)
            {
                return GetRandomMove();
            }

            return bestMove;
        }
     
        private bool CheckWinCondition(int x, int y, int player)// 辅助方法:检查是否满足胜利条件
        {
            // 定义四个方向
            int[][] directions = new int[][]
            {
        new int[] { 0, 1 },   // 水平
        new int[] { 1, 0 },   // 垂直
        new int[] { 1, 1 },   // 左上到右下
        new int[] { 1, -1 }   // 右上到左下
            };

            foreach (int[] dir in directions)
            {
                int count = 1; // 当前位置已经有一个棋子

                // 正向检查
                int dx = dir[0], dy = dir[1];
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x + i * dx;
                    int ny = y + i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard ||
                        array_chess[nx, ny] != player)
                        break;
                    count++;
                }

                // 反向检查
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x - i * dx;
                    int ny = y - i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard ||
                        array_chess[nx, ny] != player)
                        break;
                    count++;
                }

                if (count >= 5)
                    return true;
            }

            return false;
        }

        private int EvaluatePosition(int x, int y, int player)// 评估函数
        {
            int score = 0;

            // 1. 中心位置加分
            int center = size_chessboard / 2 + 1;
            int distanceToCenter = Math.Max(Math.Abs(x - center), Math.Abs(y - center));
            score += (center - distanceToCenter) * 2;

            // 2. 检查形成的连线
            int[][] directions = new int[][]
            {
        new int[] { 0, 1 }, new int[] { 1, 0 },
        new int[] { 1, 1 }, new int[] { 1, -1 }
            };

            foreach (int[] dir in directions)
            {
                int count = 1;
                int emptySides = 0;

                // 正向检查
                int dx = dir[0], dy = dir[1];
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x + i * dx;
                    int ny = y + i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard)
                        break;
                    if (array_chess[nx, ny] == player)
                        count++;
                    else if (array_chess[nx, ny] == 0)
                        emptySides++;
                    else
                        break;
                }

                // 反向检查
                for (int i = 1; i <= 4; i++)
                {
                    int nx = x - i * dx;
                    int ny = y - i * dy;
                    if (nx < 1 || nx > size_chessboard || ny < 1 || ny > size_chessboard)
                        break;
                    if (array_chess[nx, ny] == player)
                        count++;
                    else if (array_chess[nx, ny] == 0)
                        emptySides++;
                    else
                        break;
                }

                // 根据连线长度和两端空位评分
                if (count >= 5)
                    score += 100000; // 已经赢了
                else if (count == 4 && emptySides == 2)
                    score += 10000;  // 活四
                else if (count == 4 && emptySides == 1)
                    score += 1000;   // 冲四
                else if (count == 3 && emptySides == 2)
                    score += 500;    // 活三
                else if (count == 3 && emptySides == 1)
                    score += 100;    // 眠三
                else if (count == 2 && emptySides == 2)
                    score += 50;     // 活二
                else if (count == 2 && emptySides == 1)
                    score += 10;     // 眠二
            }

            return score;
        }

        private void AiDelayTimer_Tick(object sender, EventArgs e)
        {
            aiDelayTimer.Stop();
            if (!isAIThinking && !chesscheck && radioButton_AI.Checked && start)
            {
                isAIThinking = true;
                AIMove();
                isAIThinking = false;
            }
        }

        private void initialize_chessboard()//初始化棋盘
        {

            //初始化历史记录
            historical_records = new int[size_chessboard * size_chessboard][];
            for (int i = 0; i < historical_records.Length; i++)
            {
                historical_records[i] = new int[3];
            }

            // 重置棋盘数组
            for (int i = 0; i <= size_chessboard; i++)
            {
                for (int j = 0; j <= size_chessboard; j++)
                {
                    array_chess[i, j] = 0;
                }
            }

            // 计算格子尺寸
            width = (picturebox_chessboard.Width - 20) / size_chessboard;//画布的宽,预留20,除以格子数

            // 创建新的 Bitmap 缓存
            chessBoardBitmap = new Bitmap(picturebox_chessboard.Width, picturebox_chessboard.Height);

            //在 Bitmap 上绘制棋盘网格
            using (Graphics g = Graphics.FromImage(chessBoardBitmap))
            using (Pen pen = new Pen(Color.Black))
            {
                g.Clear(picturebox_chessboard.BackColor); // 清空背景

                Point pt0 = new Point(10, 10);
                for (int line = 0; line < size_chessboard; line++)
                {
                    for (int col = 0; col < size_chessboard; col++)
                    {
                        g.DrawRectangle(pen,
                            pt0.X + col * width,
                            pt0.Y + line * width,
                            width,
                            width);
                    }
                }
            }

            // 将 Bitmap 赋值给 PictureBox
            picturebox_chessboard.Image = chessBoardBitmap;

            // 重置游戏状态
            start = true;

            // 根据游戏模式设置先手
            if (radioButton_AI.Checked)
            {
                chesscheck = radioButton_AIFirst.Checked; // AI先手则黑子先行
                if (!chesscheck) // 玩家先手
                {
                    chesscheck = true;
                }
                else // AI先手
                {
                    chesscheck = false;
                    // 延迟执行AI第一次落子
                    aiDelayTimer.Stop();
                    aiDelayTimer.Start();
                }
            }
            else
            {
                chesscheck = true; // 人人对战默认白子先行
            }


        }

        private void button_start_Click(object sender, EventArgs e)//开始游戏按钮
        {
            initialize_chessboard();
        }

        private void judge_success(int x, int y)//判断是否胜利
        {
            int currentColor = array_chess[x, y];
            if (currentColor == 0) return; // (x,y)没落子,为无效检查

            // 定义四个方向:水平、垂直、左斜、右斜(使用锯齿数组)
            int[][] directions = new int[][]
            {
                new int[] { 0, 1 },   // 水平方向 (dx=0, dy=1)
                new int[] { 1, 0 },   // 垂直方向 (dx=1, dy=0)
                new int[] { 1, 1 },   // 左斜方向 (dx=1, dy=1)
                new int[] { 1, -1 }   // 右斜方向 (dx=1, dy=-1)
            };

            foreach (int[] dir in directions)
            {
                int dx = dir[0]; // 方向向量的x分量
                int dy = dir[1]; // 方向向量的y分量
                int count = 1;   // 当前子算一个

                // 向正方向检查
                int i = x + dx;
                int j = y + dy;
                while (i >= 0 && i <= size_chessboard && j >= 0 && j <= size_chessboard && array_chess[i, j] == currentColor)
                {
                    count++;
                    i += dx;
                    j += dy;
                }

                // 向反方向检查
                i = x - dx;
                j = y - dy;
                while (i >= 0 && i <= size_chessboard && j >= 0 && j <= size_chessboard && array_chess[i, j] == currentColor)
                {
                    count++;
                    i -= dx;
                    j -= dy;
                }

                // 胜利条件:连续5个同色棋子
                if (count >= 5)
                {
                    string winner = currentColor == 1 ? "白方" : "黑方";
                    MessageBox.Show($"{winner}获胜!");
                    start = false;    // 停止游戏
                    historical_records = new int[0][];
                    num_check = 0;
                    return;
                }
            }
        }

        private void picturebox_chessboard_Click(object sender, EventArgs e)//点击棋盘
        {
            if (!start) return;

            // 人人对战模式
            if (radioButton_Human.Checked)
            {
                HumanMove();
                return;
            }

            // 人机对战模式且玩家回合
            if (radioButton_AI.Checked && chesscheck)
            {
                HumanMove();
            }

        }

        private void HumanMove()//人走
        {
            try
            {
                Point box1Ponit = picturebox_chessboard.PointToClient(Control.MousePosition);
                int x = box1Ponit.X % width > width / 2 ? box1Ponit.X / width + 1 : box1Ponit.X / width;
                int y = box1Ponit.Y % width > width / 2 ? box1Ponit.Y / width + 1 : box1Ponit.Y / width;

                // 边界检查
                if (x < 1 || x > size_chessboard || y < 1 || y > size_chessboard)
                    return;

                if (array_chess[x, y] != 0)
                {
                    MessageBox.Show("请勿重复落子");
                    return;
                }

                // 在Bitmap上绘制棋子
                using (Graphics g = Graphics.FromImage(chessBoardBitmap))
                {
                    if (chesscheck) // 白子回合
                    {
                        g.FillEllipse(Brushes.White, x * width - adjust_size, y * width - adjust_size, 26, 22);
                        array_chess[x, y] = 1;
                        historical_records[num_check][0] = x;
                        historical_records[num_check][1] = y;
                        historical_records[num_check][2] = 1;
                    }
                    else // 黑子回合
                    {
                        g.FillEllipse(Brushes.Black, x * width - adjust_size, y * width - adjust_size, 26, 22);
                        array_chess[x, y] = -1;
                        historical_records[num_check][0] = x;
                        historical_records[num_check][1] = y;
                        historical_records[num_check][2] = -1;
                    }
                }

                num_check++;
                chesscheck = !chesscheck;
                picturebox_chessboard.Invalidate();
                judge_success(x, y);

                // 如果是人机对战且轮到AI
                if (radioButton_AI.Checked && !chesscheck && start)
                {
                    aiDelayTimer.Stop();
                    aiDelayTimer.Start();
                }
            }
            catch
            {
                MessageBox.Show("落子错误");
            }
        }

        private void RedrawChessboard()//悔棋专用(和初始化类似)
        {
            if (chessBoardBitmap != null) chessBoardBitmap.Dispose(); // 释放旧 Bitmap
            chessBoardBitmap = new Bitmap(picturebox_chessboard.Width, picturebox_chessboard.Height);

            using (Graphics g = Graphics.FromImage(chessBoardBitmap))
            using (Pen pen = new Pen(Color.Black))
            {
                g.Clear(picturebox_chessboard.BackColor);

                // 重新绘制棋盘
                Point pt0 = new Point(10, 10);
                for (int line = 0; line < size_chessboard; line++)
                {
                    for (int col = 0; col < size_chessboard; col++)
                    {
                        g.DrawRectangle(pen,
                            pt0.X + col * width,
                            pt0.Y + line * width,
                            width,
                            width);
                    }
                }

                // 重新绘制所有棋子
                for (int i = 0; i <= size_chessboard; i++)
                {
                    for (int j = 0; j <= size_chessboard; j++)
                    {
                        if (array_chess[i, j] == 1)//遍历棋盘,根据新的array_chess重新绘制。
                        {
                            g.FillEllipse(Brushes.White, i * width - adjust_size, j * width - adjust_size, 26, 22);
                        }
                        else if (array_chess[i, j] == -1)
                        {
                            g.FillEllipse(Brushes.Black, i * width - adjust_size, j * width - adjust_size, 26, 22);
                        }
                    }
                }
            }

            picturebox_chessboard.Image = chessBoardBitmap;
        }

        private void button_revoke_Click(object sender, EventArgs e)//悔棋按钮
        {
            if (num_check <= 0)
            {
                MessageBox.Show("无法悔棋,没有历史记录!");
                return;
            }

            num_check--;

            int x = historical_records[num_check][0];
            int y = historical_records[num_check][1];
            array_chess[x, y] = 0;

            chesscheck = !chesscheck;

            RedrawChessboard(); // 重新绘制整个棋盘

        }


    }
}

你可能感兴趣的:(c# winform 五子棋 人机对战 (详细))