2024年4月28日发(作者:)
(完整word版)C++五子棋人机对战游戏设计
(完整word版)C++五子棋人机对战游戏设计
编辑整理:
尊敬的读者朋友们:
这里是精品文档编辑中心,本文档内容是由我和我的同事精心编辑整理后发布的,发布之前我们
对文中内容进行仔细校对,但是难免会有疏漏的地方,但是任然希望((完整word版)C++五子
棋人机对战游戏设计)的内容能够给您的工作和学习带来便利。同时也真诚的希望收到您的建议
和反馈,这将是我们进步的源泉,前进的动力。
本文可编辑可修改,如果觉得对您有帮助请收藏以便随时查阅,最后祝您生活愉快 业绩进步,以
下为(完整word版)C++五子棋人机对战游戏设计的全部内容。
五子棋人机对战游戏
摘要:本文用visual c++来设计与实现简单的五子棋人机对战游戏的基本功能,玩
家可以在游戏区域中适当的位置来放棋子,通过程序设计让电脑选择最佳的落棋点,
来实现人机对战。
文中对该游戏的算法进行了详细的介绍,其中核心内容包括界面的设计、最佳落
棋位置的判断以及游戏胜利判断功能的实现。程序实现起来较简单,同时也比较实
用.
关键词: 五子棋 ,vc,人机对战游戏
1。引言
1.1 游戏介绍
五子棋是一种很受人们喜爱的智力游戏,它的规则简单,但玩法变化多端,富
有趣味性,特别锻炼人的智力,适合人们消遣.
1。2 目的
网络小游戏制作的目的是满足了人们休闲的需要,在紧张工作之余休闲类的小
游戏能够给人带来最大程度的放松,也可以增进人们之间的交流,沟通,通过游戏
还可以认识更多的朋友,也可以到达跨省、跨市,甚至跨国间人们互相娱乐的目的。
1。3 主要问题
开始制作游戏时,主要要解决的问题有以下几个方面:
1.
如何设置整个游戏的界面;
2 判断是否可以放下棋子;
3.
如何让电脑选择最佳位置;
4。 判断是黑棋胜还是白棋胜。
2.
需求分析
关于五子棋游戏的功能描述如下:运行游戏并进行初始化工作,将整个游戏
区域中纵线和横线相交的点坐标化,并且这些点是将来下棋的位置。玩家可以在
任意没有放棋子的点放下棋子,直到一方有五个棋子连成一条线为胜方.
游戏的整体运行效果如图1.1.
图1。1
3.
功能模块的设计
3。1 变量和函数
在view类中添加变量函数如下:
保存vscomputer时白棋位置 CPoint vspoint;
CPoint bpointcan4, //这个位置空,它旁边有四个黑棋
wpointcan4, //这个位置空,它旁边有四个白棋
bpointcan3, //这个位置空,它的旁边有三个黑棋
wpointcan3, //这个位置空,它的旁边有三个白棋
bpointcan2, //这个位置空,它的旁边有两个黑棋
wpointcan2, //这个位置空,它的旁边有两个白棋
bpointcan1; //不是以上情况,这个位置空
在得到最大值和方向上寻找落棋点,其中i、j表示搜索起点,n表示方向
void searchcandown1(int i,int j,int n);
void searchcandown2(int i,int j,int n);
void searchcandown3(int i,int j,int n);
void searchcandown4(int i,int j,int n);
计算最大值及方向 CPoint maxnum(int a,int b,int c,int d);
最好落棋点 void bestputdown(int i,int j);
计算机下棋 void computerdown();
在位置point放下棋子 void putdown(CPoint point);
人对机菜单 afx_msg void OnCpmputer();
3.2.框架的搭建
新建工程,选择单文档,在Step 4 of 6中先中Windows Sockets复选框
4.
算法分析与设计
4。1游戏界面的设计
由于游戏的棋盘大小是一定的,不能改变大小的,是应该符合要求的。用如下函数设置窗
口大小:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
yle=cs。dwExStyle|WS_EX_TOPMOST; //
=WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;//;
//设置窗口大小:400*340
cs。cx=450;
cs。cy=500;
return TRUE;
}
画棋盘:
在OnDraw(CDC* pDC)函数中画棋盘,由于在游戏过程中有可能重画棋盘,而那时棋
盘上面有棋子,所以,我们在这个函数里面必须有画棋子的语句。在此用数组的做为1表
示白棋,-1表示黑棋.
void CMy3_1View::OnDraw(CDC* pDC)
{
CMy3_1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//画背景
CBrush mybrush1;
SolidBrush(RGB(192,192,192));
CRect myrect1(0,0,1200,800);
pDC—〉FillRect(myrect1,&mybrush1);
//画棋盘框线
CPen mypen;
CPen*myoldPen;
Pen(PS_SOLID,1,RGB(0,0,0));
myoldPen=pDC—>SelectObject(&mypen);
for(int i=0;i<19;i++)
{
pDC—〉MoveTo(40,40+i*20);
pDC—>LineTo(400,40+i*20);
pDC—>MoveTo(40+i*20,40);
pDC—〉LineTo(40+i*20,400);
}
//重画时显示存在的棋子
CDC Dc;
if(CompatibleDC(pDC)==FALSE)
AfxMessageBox(”Can't create DC");
for(int n=0;n<19;n++)
for(int m=0;m〈19;m++)
if(wzq[n][m]==1)
{
//显示白棋
Dc。SelectObject(m_bmwhite);
pDC—>BitBlt(n*20+32,m*20+32,160,160,&Dc,0,
0,SRCCOPY);
}
else if(wzq[n][m]==—1)
{
//显示黑棋
Object(m_bmblack);
pDC—>BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,
SRCCOPY);
}
}
棋盘的效果如图
5.
核心算法
在完成界面设计后,就开始展开游戏核心的设计,该部分主要包括计算机搜索最佳
落棋位置,游戏胜利判断的实现。
5.1 搜索最佳落棋位置
计算机是怎样下棋?这就是定位的问题了.即搜索棋盘,找出一个最佳点,放下黑棋。我们
实现的方法是:全盘搜索,并把搜索到的位置,保存在变量。由于有多种情况,我们定义变量
如下:
CPoint bpointcan4, //这个位置空,它旁边有四个黑棋
wpointcan4, //这个位置空,它旁边有四个白棋
bpointcan3, //这个位置空,它的旁边有三个黑棋
wpointcan3, //这个位置空,它的旁边有三个白棋
bpointcan2, //这个位置空,它的旁边有两个黑棋
wpointcan2, //这个位置空,它的旁边有两个白棋
bpointcan1; //不是以上情况,这个位置空
并在搜索之前都赋值为(—1,-1),然后,进行搜索,并把相应的值保存在相应变量里面,
而如果前面已经对变量赋值,我们依然赋值,用新值代替旧值。注意:只保存最后一个值,这
样的一个好处是,避免了每次都从左上角开始,并且它的随机性比随机函数还随机。
全盘搜索完之后,由于上面的变量中至少有一个已经被赋值,即不是(-1,—1),可以采
用多数优先的方法,让已经有多个同色棋子的位置先下棋.
其原理是,如果已经有四个黑棋,计算机再下一个黑棋就赢了;否则,如果人已经有四个白
棋,那么计算机就必须放下一个黑棋,阻止白棋下一步赢;如果已经有三个黑棋,再下一个
黑棋,变成四个;否则,如果已经有三个白棋,下一个黑棋,破坏它;两个棋子的同理;否则,
在刚才白棋下的地方,顺便找一个位置,下棋。computerdown()函数如下:
//轮到计算机下棋
void CMy3_1View::computerdown()
{
//把各种情形赋值为如下
bpointcan4=(-1,-1);
wpointcan4=(—1,—1);
bpointcan3=(—1,—1);
wpointcan3=(—1,—1);
bpointcan2=(—1,—1);
wpointcan2=(-1,—1);
bpointcan1=(-1,-1);
//搜索最好的落棋点
for(int i=0;i〈19;i++)
for(int j=0;j〈19;j++)
bestputdown(i,j);
//判断放在哪里
//棋多的位置优先
//黑白一样多时黑先
//不是—1就表示已经被赋值!
if(bpointcan4。x!=—1)
{
putdown(bpointcan4);
return;
}
else if(wpointcan4.x!=-1)
{
putdown(wpointcan4);
return;
}
else if(bpointcan3.x!=-1)
{
putdown(bpointcan3);
return;
}
else if(wpointcan3.x!=—1)
{
putdown(wpointcan3);
return;
}
else if(bpointcan2.x!=—1)
{
putdown(bpointcan2);
return;
}
else if(wpointcan2。x!=—1)
{
putdown(wpointcan2);
return;
}
else
{
putdown(bpointcan1);
return;
}
}
上面又有两个新函数,分别定义为空函数,如下:
搜索最佳位置 void bestputdown(int i,int j);
放下黑棋 void putdown(CPoint point);
现在,对上面两个空函数进行定义
在指定位置下棋:
由于putdown(CPoint point)函数的原理非常简单,我们先说明如下:
//黑棋下
void CMy3_1View::putdown(CPoint point)
{
CDC *pDC=GetDC();
CDC Dc;
if(Dc。CreateCompatibleDC(pDC)==FALSE)
AfxMessageBox("Can’t create DC");
Object(m_bmblack);
pDC—〉BitBlt(point。x*20+32,point.y*20+32,160,160,&Dc,0,0,SRCCOPY);
wzq[point.x][point.y]=-1;
//由于原来我们检查是否结束时用的是鼠标点下的坐标,而现在
//putdown(CPoint point)函数用的是数组棋盘的坐标,所以必须转换
CPoint overpoint;
overpoint。x=point.x*20+30;
overpoint。y=point。y*20+30;
over(overpoint);
colorwhite=true;
}
搜索最佳落棋点:
现在还有void bestputdown(int i,int j)函数没有定义.它的实现原理是:在四个方
向上,各自计算那个方向上棋子的状态,利用原来定义的白棋为1,黑棋为—1的思想,让同
个方向上的五个棋子的值相加,取绝对值并赋值给为这个方向定义的局部变量num[i].
如果几个棋子是同色的,无论黑白,它的绝对值必然大,而对于几个棋子中有黑棋和白棋
的,其值必然相加而抵消变小。所以我们可以利用这种方法来寻找旁边有多个同色棋子的空
位置(前面已经具体说明)。
在每一个棋盘位置,计算以它为起点的四个方向(横、竖、撇、捺),再比较这四个方向
中哪个值最大,然后在这个方向上寻找落棋点。
//检查四个方向,各算出五个棋子的和并赋值
void CMy3_1View::bestputdown(int i,int j)
{
//四个方向的值
int num[4];
int a,k;
/////////////////////////////// num[0] —->
a=0;
if(i<15)
for(k=0;k〈5;k++)
a=a+wzq[i+k][j];
num[0]=abs(a);
////////////////////////////// num[1] "|"
a=0;
if(j<15)
for(k=0;k〈5;k++)
a=a+wzq[i][j+k];
num[1]=abs(a);
/////////////////////////////// num[2] ”"
a=0;
if(i〈15&&j〈15)
for(k=0;k<5;k++)
a=a+wzq[i+k][j+k];
num[2]=abs(a);
////////////////////////////// num[3] ”/"
a=0;
if((i〉4)&&(j<15))
for(k=0;k<5;k++)
a=a+wzq[i—k][j+k];
num[3]=abs(a);
比较哪个方向同色棋最多
由于搜索落棋点时用到最大值和方向,可以定义一个Cpoint类变量,让它返回两个
值。因为这样你就不用去写//内联函数了
CPoint numbig;
//numbig.x表示方向
//numbig。y表示最大值
numbig=maxnum(num[0],num[1],num[2],num[3]);
//在得到最大值和方向上寻找落棋点
switch(numbig.y)
{
case 4:
searchcandown4(i,j,numbig。x);break;
case 3:
searchcandown3(i,j,numbig。x);break;
case 2:
searchcandown2(i,j,numbig.x);break;
default:
searchcandown1(i,j,numbig.x);
}
}
同样的方法,为上面还没有定义的函数添加空函数。
//其中i、j表示搜索起点,n表示方向
void searchcandown1(int i,int j,int n);
void searchcandown2(int i,int j,int n);
void searchcandown3(int i,int j,int n);
void searchcandown4(int i,int j,int n);
CPoint maxnum(int a,int b,int c,int d);
最大值函数的实现:
现在先介绍CPoint maxnum(int a,int b,int c,int d)函数,它只是四个整数的比较:
CPoint CMy3_1View::maxnum(int a, int b, int c, int d)
{
//point.x为方向值
//point。y为最大值
CPoint point;
if(a〉=b)
{
point。x=0;
point。y=a;
}
else
{
point.x=1;
point.y=b;
}
if(c>point。y)
{
point。x=2;
point.y=c;
}
if(d〉point。y)
{
point.x=3;
point。y=d;
}
return point;
}
而另外的四个函数,有其相似性,分别介绍如下:
void searchcandown4(int i,int j,int n)函数:
如果最大值是四,它必然有一个空位置;可以这样计算,如果第一个是空,那我们把它赋
值给相应变量;否则,先找那个空位置,然后判断第一个棋子的颜色,并赋相应的值。
//由于相似,下面代码只解释第一个方向
//有四个同色棋
void CMy3_1View::searchcandown4(int i, int j, int n)
{
int k;
///////////////////////////// num[0] "—-”
if(n==0)
for(k=0;k〈5;k++)
//如果第一个是空
if(wzq[i][j]==0)
{
//如果下面有白棋
if(wzq[i+1][j]==1)
{
//下面位置可以下棋,已经有四个白棋
wpointcan4.x=i;
wpointcan4.y=j;
break;
}
else
{
//下面位置可以下棋,已经有四个黑棋
bpointcan4.x=i;
bpointcan4。y=j;
break;
}
}
//如果找到下棋位置,一定能找到!
else if(wzq[i+k][j]==0)
{
//如果第一个是白棋
if(wzq[j][j]==1)
{
wpointcan4。x=i+k;
wpointcan4.y=j;
break;
}
//否则第一个是黑棋
else
{
bpointcan4。x=i+k;
bpointcan4.y=j;
break;
}
}
//////////////////////////// num[1] ”|”
if(n==1)
for(k=0;k<5;k++)
{
if(wzq[i][j]==0)
if(wzq[i][j+1]==1)
{
wpointcan4。x=i;
wpointcan4。y=j;
break;
}
else
{
bpointcan4。x=i;
bpointcan4。y=j;
break;
}
else if(wzq[i][j+k]==0)
{
if(wzq[i][j]==1)
{
wpointcan4。x=i;
wpointcan4.y=j+k;
break;
}
else
{
bpointcan4.x=i;
bpointcan4。y=j+k;
break;
}
}
}
/////////////////////////////// num[2] ""
if(n==2)
for(k=0;k<5;k++)
{
if(wzq[i][j]==0)
if(wzq[i+1][j+1]==1)
{
wpointcan4.x=i;
wpointcan4。y=j;
break;
}
else
{
bpointcan4。x=i;
bpointcan4.y=j;
break;
}
else if(wzq[i+k][j+k]==0)
{
if(wzq[i][j]==1)
{
wpointcan4.x=i+k;
wpointcan4。y=j+k;
break;
}
else
{
bpointcan4。x=i+k;
bpointcan4。y=j+k;
break;
}
}
}
////////////////////////////// num[3] ”/”
if(n==3)
for(k=0;k<5;k++)
{
if(wzq[i][j]==0)
if(wzq[i-1][j+1]==1)
{
wpointcan4.x=i;
wpointcan4。y=j;
break;
}
else
{
bpointcan4。x=i;
bpointcan4.y=j;
break;
}
else if(wzq[i-k][j+k]==0)
{
if(wzq[i][j]==1)
{
wpointcan4.x=i—k;
wpointcan4。y=j+k;
break;
}
else
{
bpointcan4.x=i—k;
bpointcan4.y=j+k;
break;
}
}
}
}
void searchcandown3(int i,int j,int n)函数:
如果最大值是三,它有两种情况,一种是三个同色和两个空;一种是四个同色和一个异色。
前一种必定能找到一个空位置,赋值;后一种必定找不到空位置,不赋值.所以我们的想法很
简单,先找到空位置,证明有三个同色,这对于玩五子棋来说三个同色是很重要的,再判断是
哪种颜色,赋相应的值。
void searchcandown2(int i,int j,int n)函数:
如果最大值是二,也有两种情况:一种是有两个同色和三个空位置;一种是有三个同色和
一个异色和一个空位置,并且只算三个同色不连在一起的情况(因为如果有三个连续的情况,
重全盘搜索的角度看,必然会被另外的情况所代替)。
分两种算法:一种是有一个空位置,一种是有三个空位置。前者先找到空位置,再判断它
下面两个是否同色,同色则赋值给相应变量,异色则不赋值,因为意义不大;后者只要找到一
个空位置就行了.
void searchcandown1(int i,int j,int n)函数:
最后的一个函数是searchcandown1(int i, int j, int n),这是为了预防用的,如果
以上情况不发生,这个函数就是为了处理这种情况。这里就说明了为什么我们要添加vspoint
的原因了,它保存了上次白棋下棋的位置,而我们在没有办法的情况下,在白棋旁边随便找个
位置就可以了。
//如果五个位置的和是一
void CMy3_1View::searchcandown1(int i, int j, int n)
{
//计算刚才白棋落棋点
int ii=(vspoint.x-30)/20;
int jj=(vspoint.y-30)/20;
int a;
for(a=0;a〈5;a++)
//如果不到边界
if(ii+a<19)
{
//向右,如果有空位置
if(wzq[ii+a][jj]==0)
{
//在这个位置下黑棋
bpointcan1。x=ii+a;
bpointcan1。y=jj;
return;
}
}
//到了边界
else
//向左,如果有空位置
if(wzq[ii—1][jj]==0)
{
bpointcan1.x=ii-a;
bpointcan1。y=jj;
return;
}
}
5。2 判断游戏是否结束
用一个over()函数判断是否结束,是则结束并重新开始;否则,接着把鼠标变成棋
子,表示继续下棋。
此函数是利用刚下棋的位置为中心,检查它各个方向上的连续五个棋子是否同色,是则
结束并重新开始。
利用连续五个棋子的值相加,如果它们的值的绝对值等于5,则说明是同色。
void CMy3_1View::over(CPoint point)
{
//获取鼠标指向数组位置,即中心位置
int x=(point。x-30)/20;
int y=(point.y—30)/20;
//计算开始判断的坐标 xx,yy
int xx,yy;
if(x〈4)
xx=0;
else
xx=x—4;
if(y〈4)
yy=0;
else
yy=y—4;
int i,j,a;
//横向判断
for(i=xx;i<15;i++)
{
a=0;
for(j=i;j〈i+5;j++)
{
a=a+wzq[j][y];
//五个都是白棋
if(a==5)
{
AfxMessageBox(”白棋胜!”);
//重新开始
OnStart();
return;
}
//五个都是黑棋
if(a==—5)
{
AfxMessageBox(”黑棋胜!");
OnStart();
return;
}
}
}
//竖向判断
for(i=yy;i<15;i++)
{
a=0;
for(j=i;j〈i+5;j++)
{
a=a+wzq[x][j];
if(a==5)
{
AfxMessageBox("白棋胜!");
OnStart();
return;
}
if(a==-5)
{
AfxMessageBox(”黑棋胜!");
OnStart();
return;
}
}
}
//向右下角
//判断起点位置
if(x { if(xx==0) yy=y-x; } else { if(yy==0) xx=x—y; } //参数over=1时退出循环 int over=0; do { a=0; for(i=0;i<5;i++) { if((xx+i)<19||(yy+i)〈19) { a=a+wzq[xx+i][yy+i]; if(a==5) { AfxMessageBox("白棋胜!"); OnStart(); return; } if(a==—5) { AfxMessageBox(”黑棋胜!"); OnStart(); return; } } //到了边界 else over=1; } xx+=1; yy+=1; }while(over==0); //向左下角 if(y〉(18—x)) { if(x〉13) { yy=y—(18-x); xx=18; } else { yy=y—4; xx=x+4; } } else { if(y<5) { xx=x+y; yy=0; } else { yy=y—4; xx=x+4; } } over=0; do { a=0; for(i=0;i〈5;i++) { if((xx—i)>=0||(yy+i)〈19) { a=a+wzq[xx-i][yy+i]; if(a==5) { AfxMessageBox(”白棋胜!”); OnStart(); return; } if(a==-5) { AfxMessageBox(”黑棋胜!”); OnStart(); return; } } //到了边界 else over=1; } xx-=1; yy+=1; }while(over==0); } 总结 本文用visual c++来设计与实现简单的五子棋游戏的基本功能,对该游戏的算 法进行详细的介绍,其中核心内容包括最佳落棋位置判断,游戏胜利判断以及整个 游戏用户交换功能的实现。算法比较简单,运行速度快。但是,游戏实现的功能单 一化及界面较简单,可以考虑不同难度系数的游戏,设置多种级别。
发布评论