模块一
一、首先在Program.cs中设置初始运行窗体为InitialForm,代码如下:
代码位置:gobang\Gobang\Program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new InitialForm());
}
二、然后为名为StartGame的button控件添加单击事件,用于显示选择模式窗口,代码如下:
代码位置:gobang\Gobang\初始界面.cs
private void StartGame_Click(object sender, EventArgs e)
{
SelectModesForm selectModesForm = new SelectModesForm();//创建选择模式窗体对象
this.Visible = false;//使初始见面不可见
selectModesForm.ShowDialog();//显示选择模式窗口
this.Visible = true;//使其可见
}
模块二
一、首先为名为StartGame的button控件添加单击事件,用于显示双人游戏窗口,代码如下:
代码位置:gobang\Gobang\选择模式窗口.cs
private void StartGame_Click(object sender, EventArgs e)
{
TwoPlayersForm twoPlayersForm = new TwoPlayersForm();
this.Visible = false;
twoPlayersForm.ShowDialog();
this.Visible = true;
}
二、然后为名为button1的button控件添加单击事件,用于显示人机游戏窗口,代码如下:
代码位置:gobang\Gobang\选择模式窗口.cs
private void button1_Click(object sender, EventArgs e)
{
AiPlayersForm aiPlayersForm = new AiPlayersForm();
this.Visible = false;
aiPlayersForm.ShowDialog();
this.Visible = true;
}
三、最后为名为button2的button控件添加单击事件,用于返回主界面,代码如下:
代码位置:gobang\Gobang\选择模式窗口.cs
private void button2_Click(object sender, EventArgs e)
{
this.Visible = false;
InitialForm initialForm = new InitialForm();
initialForm.Visible = true;
}
模块三
一、定义枚举类型PieceType用于获取棋子类型,代码如下:
代码位置:Gobang\Gobang\PieceType.cs
namespace Gobang
{
enum PieceType//棋子类型
{
BLACK, WHITE, NONE//黑棋、白棋、无棋子
}
}
二、首先定义棋子类Piece用于创建棋子,代码如下:
代码位置:Gobang\Gobang\Piece.cs
using System.Drawing; //提供对 GDI + 基本图形功能的访问权限
namespace Gobang
{
abstract class Piece : PictureBox //创建抽象棋子类继承与PictureBox,防止意外建立新Piece对象
{
private static readonly int IMAGE_WIDTH = 50; //定义棋子宽度
public Piece (int x, int y) //棋子构造函数
{
this.BackColor = Color.Transparent; //背景为透明
this.Location = new Point(x-IMAGE_WIDTH/2, y-IMAGE_WIDTH/2); //棋子坐标
this.Size = new Size(IMAGE_WIDTH, IMAGE_WIDTH);//棋子大小
}
public abstract PieceType GetPieceType();//多态获得棋子类型
public abstract Image GetNextImage();//多态获得下一个棋子的图片
}
}
三、定义黑棋类BlackPiece,代码如下:
代码位置:Gobang\Gobang\BlackPiece.cs
using System.Drawing;//提供对 GDI + 基本图形功能的访问权限
namespace Gobang
{
class BlackPiece : Piece//继承自Piece类
{
public BlackPiece(int x , int y) : base (x , y) //把x,y坐标传入基类Piece的构造函数中
{
this.Image = Properties.Resources.black; //指定黑棋图片
}
public override PieceType GetPieceType()//改写基类Piece中的GetPieceType方法
{
return PieceType.BLACK;//返回棋子类型为黑棋
}
public override Image GetNextImage()//改写基类Piece中的GetNextImage方法
{
return Properties.Resources.white;//返回下一个棋子的图片
}
}
}
四、定义白棋类WhitePiece,代码如下:
代码位置:Gobang\Gobang\BlackPiece.cs
using System.Drawing;//提供对 GDI + 基本图形功能的访问权限
namespace Gobang
{
class WhitePiece : Piece//继承自Piece类
{
public WhitePiece(int x, int y) : base(x, y)//把x,y坐标传入基类Piece的构造函数中
{
this.Image = Properties.Resources.white;//指定白棋图片
}
public override PieceType GetPieceType()//改写基类Piece中的GetPieceType方法
{
return PieceType.WHITE;//返回棋子类型为白棋
}
public override Image GetNextImage()//改写基类Piece中的GetNextImage方法
{
return Properties.Resources.black;//返回下一个棋子的图片
}
}
}
五、定义棋盘类Board用于存贮当前棋盘信息,代码如下:
代码位置:Gobang\Gobang\Board.cs
同样需要System.Drawing命名空间 using System.Drawing;
Ⅰ、成员变量
public static readonly int NODE_COUNT = 15; //棋盘规格
public static readonly int STEP_SPAN_TIME = 30;//下棋间隔时间
private static readonly Point NO_MATCH_NODE = new Point(-1, -1);// 设置无匹配点
private static readonly int NODE_RADIUS = 20; //点的半径
public static readonly int OFFSET = 35; //棋盘距离边界的距离
public static readonly int NODE_DISTANCE = 70; //每个结点之间的距离
public Piece[,] pieces = new Piece[NODE_COUNT, NODE_COUNT]; //创建存贮棋子的二维数组
private static Point lastPlacedNode = NO_MATCH_NODE;//最后一个棋子
Ⅱ、成员函数
1、为了使棋子落在正确的位置上,需要找到距离用户鼠标点击位置最近的结点
1)首先在一维方向找到最近结点,代码如下:
public int FindTheClosetNode(int pos)
{
pos -= OFFSET;//坐标点减去棋盘边界
int quotient = pos / NODE_DISTANCE; //求商数
int remainder = pos % NODE_DISTANCE; //求余数
if (remainder <= NODE_RADIUS) //如果余数小于所给半径范围
return quotient;//返回商数即前一个点
else if (remainder >= NODE_DISTANCE - NODE_RADIUS) //如果余数大于所给半径范围且属于后一个点的范围内
return quotient + 1;//返回后一个点
else //没有符合点
return -1;//返回无匹配点
}
2)然后在二维方向找到最近结点,代码如下:
public Point FindTheClosetNode(int x, int y)
{
int nodeX = FindTheClosetNode(x); //判断横向
if (nodeX == -1)
return NO_MATCH_NODE;//返回无匹配点
int nodeY = FindTheClosetNode(y); //判断纵向
if (nodeY == -1)
return NO_MATCH_NODE;//返回无匹配点
return new Point(nodeX, nodeY); //放回坐标
}
2、得到最近落子结点后需要获取棋子真正的坐标位置,进行反运算,代码如下:
public Point ConverToTruePosition(Point node)
{
Point TruePosition = new Point();//定义真正位置坐标点
TruePosition.X = node.X * NODE_DISTANCE + OFFSET;//横向进行反运算
TruePosition.Y = node.Y * NODE_DISTANCE + OFFSET;//纵向进行反运算
return TruePosition;//返回坐标点
}
3、放置棋子操作,代码如下:
public Piece PlacePiece(int x, int y, PieceType type)
{
Point node = FindTheClosetNode(x, y); //找到最近的点
if (node == NO_MATCH_NODE) //如果没有最近点,不可放棋子,返回null
return null;
if (pieces[node.X, node.Y] != null) //如果有最近点,检查是否有棋子存在
return null;
Point truepos = ConverToTruePosition(node); //将node坐标转换为真正坐标
if (type == PieceType.BLACK) //如果没有棋子则把黑白棋子坐标存入二维数组中
pieces[node.X, node.Y] = new BlackPiece(truepos.X, truepos.Y);//存入黑棋
else if (type == PieceType.WHITE)
pieces[node.X, node.Y] = new WhitePiece(truepos.X, truepos.Y);//存入白棋
lastPlacedNode = node; //记录最后棋子的位置
return pieces[node.X, node.Y]; //取出二维数组中的棋子
}
4、获得最后一个棋子,代码如下:
public Point LastPlacedNode { get { return lastPlacedNode; } }
5、判断是否可放置棋子,代码如下:
public bool CanBePlaced(int x, int y)
{
Point node = FindTheClosetNode(x, y); //找到最近的点
if (node == NO_MATCH_NODE) //如果没有最近点,不可放棋子,返回false
return false;
if (pieces[node.X, node.Y] != null) //如果有最近点,检查是否有棋子存在
{
return false;
}
return true;
}
6、获取棋子类型操作,代码如下:
public PieceType GetPieceType(int nodeX, int nodeY)
{
if (pieces[nodeX, nodeY] == null)//如果棋子不存在,则返回无棋子
return PieceType.NONE;
else
return pieces[nodeX, nodeY].GetPieceType();//否则返回棋子类型
}
7、AI判断是否可放棋子,代码如下:
public bool AICanBePlaced(int x,int y)
{
int nodeX = FindTheClosetNode(x); //判断横向
if (nodeX == -1)
return false;
int nodeY = FindTheClosetNode(y); //判断纵向
if (nodeY == -1)
return false;
else
return true;
}
8、重新游戏操作,代码如下:
public void Reload()
{
lastPlacedNode = NO_MATCH_NODE;//设置最后一个棋子位置为无匹配点
pieces = new Piece[NODE_COUNT, NODE_COUNT];//新建存贮棋子的二维数组
}
六、定义GameManage类用于实现游戏的核心机制
代码位置:Gobang\Gobang\GameManage.cs
同样需要System.Drawing命名空间 using System.Drawing;
Ⅰ、成员变量
public static int CurrentTime = Board.STEP_SPAN_TIME;// 设置当前剩余时间,默认当前剩余时间为下棋间隔时间
private Board board = new Board();//创建board对象用于调用Board类中的方法,并进行重构
private PieceType CurrentPlayer = PieceType.BLACK;//默认当前玩家为黑棋
public static PieceType PastPlayer = PieceType.NONE;//默认上一步玩家为NONE
private PieceType winner = PieceType.NONE;//默认赢家为NONE
private int[,] Score = new int[Board.NODE_COUNT, Board.NODE_COUNT];//记录分数
private List<Point> MaxPoint = new List<Point>();//记录得分最高点
Ⅱ、成员函数
1、判断是否可下棋,重构Borad中的方法,代码如下:
public bool CanBePlaced(int x, int y)
{
return board.CanBePlaced(x, y);
}
2、下棋操作,重构Borad中的方法,并且实现交换棋手的功能,代码如下:
public Piece PlacePiece(int x, int y)
{
Piece piece = board.PlacePiece(x, y, CurrentPlayer);//调用board的下棋操作
if (piece != null)//如果可放棋子
{
//交换选手
if (CurrentPlayer == PieceType.BLACK)//如果当前玩家为黑棋
{
PastPlayer = PieceType.BLACK;//设置上一名玩家为黑棋
CurrentPlayer = PieceType.WHITE;//并指定当前玩家为白棋
}
else if (CurrentPlayer == PieceType.WHITE)//如果当前玩家为白棋
{
PastPlayer = PieceType.WHITE;//设置上一名玩家为白棋
CurrentPlayer = PieceType.BLACK;//并指定当前玩家为黑棋
}
return piece;//传出棋子
}
return null;
}
3、检查是否有玩家获胜,代码如下:
public void CheckWiner()
{
int centerX = board.LastPlacedNode.X;//获得中心点 (即最后一个棋子)的横坐标
int centerY = board.LastPlacedNode.Y;//获得中心点 (即最后一个棋子)的纵坐标
//xDir yDir是循环固定的方向 checkxDir checkyDir是查找的方向
for (int xDir = -1; xDir <= 1; xDir++) {
for (int yDir = -1; yDir <= 1; yDir++) {
//排除center棋子
if (xDir == 0 && yDir == 0)
continue;
//增加反向查找变量
int reverse = 0; //反向的次数,默认为零,最多反向一次
int checkxDir = xDir;//动态查找和移动的方向X,可能会出现反向
int checkyDir = yDir;//动态查找和移动的方向Y,可能会出现反向
int count = 1; //记录检查到同色连续的棋子,自己算一个
int step = 0; //记录动态查找需要的步数,最多四次
int distance = 1; //记录动态查找点和中心点之间的距离的绝对值,从一开始
while (step < 4)
{
step++;//查找步数加一
int targetX = centerX + distance * checkxDir;//获得所要检查位置横坐标
int targetY = centerY + distance * checkyDir;//获得所要检查位置纵坐标
//如果颜色不同,或超出边界,则进行反向查找,查找不做记录,距离恢复到一
if (targetX < 0 || targetX >= Board.NODE_COUNT ||
targetY < 0 || targetY >= Board.NODE_COUNT ||
board.GetPieceType(targetX, targetY) != PastPlayer)
{
reverse++;//反向次数加一
checkxDir = -checkxDir;//X坐标反向
checkyDir = -checkyDir;//Y坐标反向
step--;//查找步数不算-1
distance = 1;//记录恢复到1
}
else //如果未出边界,且是同色棋子,则棋子个数加一同时距离加一
{
count++;//棋子个数加1
distance++;//距离加1
}
if (reverse == 2) //当反向次数为二时左右都没有同色棋子,退出本次循环
{ break; }
}
if (count == 5)//检查是否有五颗相同棋子相邻
{
winner = PastPlayer;//上一名玩家胜利
return;
}
if(CurrentTime==0)//检查是否当前剩余时间为零
{
winner = PastPlayer;//上一名玩家胜利
return;
}
}
}
}
4、返回赢家,代码如下:
public PieceType Winner { get { return winner; }set { winner = value; } }
5、对当前棋盘空位进行评分,代码如下:
public void CalculateScore()
{
int PersonNum = 0;//玩家棋子数目
int BotNum = 0;//AI棋子数目
int EmptyNum = 0;//空棋子数目
Array.Clear(Score, 0, Score.Length);//初始化记录分数数组
//遍历整个棋盘,对每个可落子点进行评分
for (int row = 0; row < Board.NODE_COUNT; row++)
for(int col=0; col<Board.NODE_COUNT; col++ )
if(row>=0&&col>=0&&board.pieces[row,col]==null)//如果在此位置无棋子
{
//则根据此空位四周八个方向的棋子分布情况对其进行评分
for(int y=-1;y<=1; y++)
for(int x=-1;x<=1;x++)
{
PersonNum = 0;
BotNum = 0;
EmptyNum = 0;
if (!(x==0&&y==0))//除去评分点
{
//对黑棋
for (int i = 1; i <= 4; i++)//对各个正方向上邻接的四个位置进行统计
{
if (row + i * y > 0 && row + i * y < Board.NODE_COUNT &&
col + i * x > 0 && col + i * x < Board.NODE_COUNT &&
board.GetPieceType(row + i * y
, col + i * x) == PieceType.BLACK)//如果统计的位置在棋盘范围内并且是黑棋
{
PersonNum++;//玩家棋子数加1
}
else if (row + i * y > 0 && row + i * y < Board.NODE_COUNT &&
col + i * x > 0 && col + i * x < Board.NODE_COUNT &&
board.GetPieceType(row + i * y
, col + i * x) == PieceType.NONE)//如果统计的位置在棋盘范围内并且无棋子
{
EmptyNum++;//空棋子数加1
break;
}
else
break;
}
for (int i = 1; i <= 4; i++)//对各个负方向上邻接的四个位置进行统计
{
if (row - i * y > 0 && row - i * y < Board.NODE_COUNT &&
col - i * x > 0 && col - i * x < Board.NODE_COUNT &&
board.GetPieceType(row - i * y
, col - i * x) == PieceType.BLACK)//如果统计的位置在棋盘范围内并且是黑棋
{
PersonNum++;//玩家棋子数加1
}
else if (row - i * y > 0 && row - i * y < Board.NODE_COUNT &&
col - i * x > 0 && col - i * x < Board.NODE_COUNT &&
board.GetPieceType(row - i * y,
col - i * x) == PieceType.NONE)//如果统计的位置在棋盘范围内并且无棋子
{
EmptyNum++;//空棋子数加1
break;
}
else
break;
}
if (PersonNum == 1) //杀二(空位周围有一颗黑棋)加10分
{
Score[row, col] += 10;
}
else if (PersonNum == 2) //杀三(空位周围有两颗黑棋在一条直线上)
{
if (EmptyNum == 1)
Score[row, col] += 30;//如果有一个空位加30分
else if (EmptyNum == 2)
Score[row, col] += 40;//如果有两个空位加40分
}
else if (PersonNum == 3) //杀四(空位周围有三颗黑棋在一条直线上)
{
if (EmptyNum == 1)
Score[row, col] += 60;//如果有一个空位加60分
else if (EmptyNum == 2)
Score[row, col] += 110;//如果有两个空位加110分
}
else if (PersonNum == 4)//杀五(空位周围有四颗黑棋在一条直线上)加10100分
Score[row, col] += 10100;
EmptyNum = 0;//空棋子数置零
//对白棋
for (int i = 1; i <= 4; i++)//对各个正方向上邻接的四个位置进行统计
{
if (row + i * y > 0 && row + i * y < Board.NODE_COUNT &&
col + i * x > 0 && col + i * x < Board.NODE_COUNT &&
board.GetPieceType(row + i * y,
col + i * x) == PieceType.WHITE)//如果统计的位置在棋盘范围内并且是白棋
{
BotNum++;//AI棋子数加一
}
else if (row + i * y > 0 && row + i * y < Board.NODE_COUNT &&
col + i * x > 0 && col + i * x < Board.NODE_COUNT &&
board.GetPieceType(row + i * y
, col + i * x) == PieceType.NONE)//如果统计的位置在棋盘范围内并且无棋子
{
EmptyNum++;//空棋子数加一
break;
}
else
break;
}
for (int i = 1; i <= 4; i++)//对各个负方向上邻接的四个位置进行统计
{
if (row - i * y > 0 && row - i * y < Board.NODE_COUNT &&
col - i * x > 0 && col - i * x < Board.NODE_COUNT &&
board.GetPieceType(row - i * y
, col - i * x) == PieceType.WHITE)//如果统计的位置在棋盘范围内并且是白棋
{
BotNum++;//AI棋子数加一
}
else if (row - i * y > 0 && row - i * y < Board.NODE_COUNT &&
col - i * x > 0 && col - i * x < Board.NODE_COUNT &&
board.GetPieceType(row - i * y,
col - i * x) == PieceType.NONE)//如果统计的位置在棋盘范围内并且无棋子
{
EmptyNum++;//空棋子数加一
break;
}
else
break;
}
if (BotNum == 0)//普通下棋加5分
{
Score[row, col] += 5;
}
else if (BotNum == 1)//活二(空位周围有一颗白棋且邻接位置无棋子)加10分
{
Score[row, col] += 10;
}
else if (BotNum == 2)
{
if (EmptyNum == 1)//死三(空位周围有两颗白棋在一条直线上且只有一个空位置)加25分
Score[row, col] += 25;
else if (EmptyNum == 2)//活三(空位周围有两颗白棋在一条直线上且有两个空位置)加50分
Score[row, col] += 50;
}
else if (BotNum == 3)//如果空位周围有三颗白棋在一条直线上
{
if (EmptyNum == 1)//死四(只有一个空位置)加55分
Score[row, col] += 55;
else if (EmptyNum == 2)//活四(有两个空位置)加100分
Score[row, col] += 100;
}
else if (BotNum >= 4)//如果空位周围有四个以上的白棋在一条直线上加20000分
Score[row, col] += 20000;
}
}
}
}
6、AI走棋功能,代码如下:
public Point AiPlacePiece()
{
CalculateScore();//首先对当前棋盘进行评分
int MaxScore = 0;//设置最高分数,初始化为0
//遍历当前棋盘上的每一个空位置,并记录分数最高的点
for (int row = 0; row < Board.NODE_COUNT; row++)
{
for (int col = 0; col < Board.NODE_COUNT; col++)
{
Point AiPoint = new Point();
if (board.pieces[row, col] == null)//如果是空位置
{
if (Score[row, col] > MaxScore)//如果当前点分数大于最高分数
{
MaxPoint.Clear();//清空记录分数最高点的List表
MaxScore = Score[row, col];//赋予新的最高分数
AiPoint = new Point(row * Board.NODE_DISTANCE + Board.OFFSET,
col * Board.NODE_DISTANCE + Board.OFFSET);//赋给该点的真正坐标位置
MaxPoint.Add(AiPoint);//记录到分数最高点的List表中
}
else if (Score[row, col] == MaxScore)//如果当前分数与最高分数相等,直接记录该位置
{
AiPoint = new Point(row * Board.NODE_DISTANCE + Board.OFFSET,
col * Board.NODE_DISTANCE + Board.OFFSET);
MaxPoint.Add(AiPoint);
}
}
}
}
//因为可能有多个空位置的分数都为最高分数,所以用随机数随机选定一个空位置
Random ran = new Random();
int a=ran.Next(0, MaxPoint.Count);
MaxPoint.ToArray();
return MaxPoint[a];
}
7、悔棋功能
1)重新交换棋手,代码如下:
public void BackChange()
{
if (CurrentPlayer == PieceType.BLACK)
{
PastPlayer = PieceType.BLACK;
CurrentPlayer = PieceType.WHITE;
}
else if (CurrentPlayer == PieceType.WHITE)
{
PastPlayer = PieceType.WHITE;
CurrentPlayer = PieceType.BLACK;
}
}
2)删除上一个棋子的位置信息,代码如下:
public void Delete (Point p)
{
Point node= board.FindTheClosetNode(p.X, p.Y);
board.pieces[node.X, node.Y] = null;//置空
}
8、重新游戏功能,代码如下:
public void Reload()
{
CurrentPlayer = PieceType.BLACK;//重置当前玩家为
winner = PieceType.NONE;
board.Reload();
CurrentTime = Board.STEP_SPAN_TIME+1;
}
七、在用户实际使用过程中,方法和控件结合使用的具体实现:
代码位置:gobang\Gobang\人机游戏界面.cs
同样需要System.Drawing命名空间 using System.Drawing;
1、全局变量的定义:
private GameManage game = new GameManage();//用于调用GameManage类中的相关方法
private Board board = new Board();//用于调用Board类中的相关方法
List<Piece> Pieces = new List<Piece>();//用于存放已经下过的棋子
List<Point> Points = new List<Point>();//用于存放已经下过棋子对应的坐标位置
2、首先在窗体加载事件中设置定时器的时间间隔
private void AiPlayersForm_Load(object sender, EventArgs e)
{
this.timer.Interval = 1000;//为倒计时服务,1000ms
this.timercheck.Interval = 10;//为检查是否有玩家获胜服务,10ms
}
3、用户点击"开始游戏"功能实现:
private void BtnStart_Click_1(object sender, EventArgs e)
{
this.panel2.Enabled = true;//允许棋盘与用户交互
this.timer.Enabled = true;//运行定时器
this.timercheck.Enabled = true;//运行定时器
}
4、设置两个Timer的Tick事件
1)对于timer要求实现倒计时功能:
private void timer_Tick(object sender, EventArgs e)
{
GameManage.CurrentTime -= 1;//每个过1000ms当前倒计时减1
this.LabTime.Text = GameManage.CurrentTime.ToString().Trim();//更新倒计时
}
2)timercheck要求实现检查当前是否有玩家胜利并给出提示信息功能:
private void timercheck_Tick_1(object sender, EventArgs e)
{
game.CheckWiner();//检查是否有玩家获胜
if (game.Winner == PieceType.BLACK)//如果赢家是黑棋
{
//停止定时器
timer.Stop();
timercheck.Stop();
//显示当前赢家信息并给出提示信息"玩家获胜!"
this.LabCurrentPlayer.Text = "获胜者:";
this.PicCurrentPlayer.Image = Properties.Resources.black;
MessageBox.Show("玩家获胜!", "游戏结束");
}
else if (game.Winner == PieceType.WHITE)//如果赢家是白棋
{
//停止定时器
timer.Stop();
timercheck.Stop();
//显示当前赢家信息并给出提示信息"电脑获胜!"
this.LabCurrentPlayer.Text = "获胜者:";
this.PicCurrentPlayer.Image = Properties.Resources.white;
MessageBox.Show("电脑获胜!", "游戏结束");
}
}
5、在用户鼠标移动时,提示用户哪个位置可落子功能:
private void panel2_MouseMove_1(object sender, MouseEventArgs e)
{
if (game.CanBePlaced(e.X, e.Y))//如果可放置棋子
this.Cursor = Cursors.Hand;//鼠标标识变为手形光标
else
this.Cursor = Cursors.Default;//否则变为箭头光标
}
6、用户点击棋盘实现下子功能:
private void panel2_MouseDown_1(object sender, MouseEventArgs e)
{
if (game.Winner != PieceType.NONE)//如果已经有赢家则不可再继续下棋
{
return;
}
Piece piece = game.PlacePiece(e.X, e.Y);//获得棋子
if (piece != null)//如果当前位置无棋子,可下
{
Pieces.Add(piece);//记录到List<Piece>Pieces中
Points.Add(e.Location);//记录到 List<Point> Points中
this.panel2.Controls.Add(piece);//在棋盘上生成棋子
this.PicCurrentPlayer.Image = piece.GetNextImage();//显示下一个玩家的图像
GameManage.CurrentTime = Board.STEP_SPAN_TIME + 1;//间隔时间恢复至初始值
}
}
7、在用户下完棋抬起鼠标后AI走棋
private void panel2_MouseUp_1(object sender, MouseEventArgs e)
{
if (board.AICanBePlaced(e.X, e.Y))//判断当前位置是否可下棋
{
if (game.Winner != PieceType.NONE)//如果已经有赢家则不可再继续下棋
{
return;
}
//获得分数最高棋子的位置信息
Point Aipoint = new Point();
Aipoint.X = game.AiPlacePiece().X;
Aipoint.Y = game.AiPlacePiece().Y;
Piece piece = game.PlacePiece(Aipoint.X, Aipoint.Y);
if (piece != null)
{
Pieces.Add(piece);//记录到List<Piece>Pieces中
Points.Add(Aipoint);//记录到 List<Point> Points中
this.panel2.Controls.Add(piece);//在棋盘上生成棋子
this.PicCurrentPlayer.Image = piece.GetNextImage();//显示下一个玩家的图像
GameManage.CurrentTime = Board.STEP_SPAN_TIME + 1;//间隔时间恢复至初始值
}
}
}
8、用户点击悔棋按钮实现的功能
private void Back_Click(object sender, EventArgs e)
{
//对上一个棋子(人机棋子)
this.panel2.Controls.Remove(Pieces[Pieces.Count-1]);//从棋盘上删除棋子图片
game.Delete(Points[Points.Count - 1]);//在棋盘信息中删除棋子的位置信息
//对上上一个棋子(玩家棋子)
this.panel2.Controls.Remove(Pieces[Pieces.Count - 2]);//从棋盘上删除棋子图片
game.Delete(Points[Points.Count - 2]);//在棋盘信息中删除棋子的位置信息
}
9、用户点击认输按钮实现的功能
private void GiveUp_Click(object sender, EventArgs e)
{
//给出提示信息,当用户确定后设置赢家为上一名玩家
if (MessageBox.Show("确定要认输吗", "认输",
MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
{
game.Winner = GameManage.PastPlayer;
}
}
10、当用户点击重新游戏按钮实现的功能
private void BtnReload_Click_1(object sender, EventArgs e)
{
game.Reload();//重置棋盘信息
//重置当前玩家为黑棋
this.LabCurrentPlayer.Text = "当前玩家:";
this.PicCurrentPlayer.Image = Properties.Resources.black;
this.panel2.Controls.Clear(); //清空棋盘上的棋子
this.LabTime.Text = Board.STEP_SPAN_TIME.ToString();//设置倒计时为初始时间
//重新启动定时器
timer.Start();
timercheck.Start();
}
模块四(由于本模块与模块三实现方法基本类似,在此仅在不同之处做出说明)
不同点一:因为是双人游戏不需人机走棋,不用添加MouseUp事件
不同点二:悔棋功能的设计是针对悔棋方已下过棋,而对手玩家未下棋之前实现的。代码稍作改动:
private void Back_Click(object sender, EventArgs e)
{
this.panel1.Controls.Remove(Pieces[Pieces.Count - 1]);//从棋盘上删除上一名玩家(悔棋方)的棋子
game.Delete(Points[Points.Count - 1]);//从棋盘信息中删除上一名玩家(悔棋方)的棋子信息
game.BackChange();//重新交换玩家
this.PicCurrentPlayer.Image = Pieces[Pieces.Count - 2].GetNextImage();//设置当前玩家为悔棋方
}
不同点三:在检查获胜方时,黑棋就代表黑棋玩家,白棋就代表白棋玩家。