目录
1.前言
2.人机对战主要功能实现
3.其他功能修改
4.完整代码
c# winform 简单五子棋,支持连续悔棋。-CSDN博客
基础版跳链接。建议先阅读。
在基础版的界面上增加两个groupBox,并各自放两个radioButton。
在基础版上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;
其中,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;
}
}
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
),则直接返回该位置。score -= 50
)。GetRandomMove()
)。EvaluatePosition() 评估方法:
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 类似。
原版的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; // 玩家先手(白子)
}
}
}
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(); // 重新绘制整个棋盘
}
}
}