#include<cstdlib>
using namespace std
class CHESS
{
public:
CHESS()
void setStep(bool&ipjudge)//双人对战轮流走棋函数
void setStepC(bool&ipjudge)//人机对战走棋函数
void coutChess()//输出棋盘
void coutPW()//输出权值表
bool getTurn(){flag=!flagreturn flag}//轮流走棋控制函数
void flushChess()//刷新棋盘信息函数
void judgeWin()//判断是否赢棋函数
void winer()//赢家输出函数
int getAns(){return result}//返回结果(赢家判断)
static int count//走棋步数变量
private:
bool flag//轮流走棋判断变量
int PW[16][16],tPW[4]//权值变量,最高权值变量
int result,num[2]//结果(赢家判断),玩家输入棋子坐标判断
char inPut[2],temp[2]//玩家输入数据,转换暂存数据
char CBoard[16][16]//棋盘数据
int judgeAWin(int a,int b)//判断是否A为赢家函数
int judgeBWin(int a,int b)//判断是否B为赢家函数
void cSetStep()//电脑走棋函数
void setPower()//初始化权值函数
int adddepth(int depth)//填充权值函数
void judgePw(int x,int y,int direction,int depth,char test)//棋子判断[x坐标,y坐标,方向(顺时针0-无,1-左,2-左上,3-上,4-右上),深度(depth),标识符(A/B)]
void getFinalPw()
//权值判断函数
}
int CHESS::count=0
void VsComputer() //人机对战
void VsPlayer()//双人对战
int main()
{
int choose
CHESS newP
do
{
choose=0
system("cls")
cout<<" 欢乐五子棋"<<endl<<endl
cout<<"请选择:"<<endl<<endl
cout<<"1:人机对战模式"<<endl<<endl
cout<<"2:双人对战模式"<<endl<<endl
cout<<"3:退出游戏"<<endl<<endl<<endl
cout<<"**************"<<endl
cout<<"**************"<<endl<<endl<<endl<<endl
cout<<"请输入你的选择:"
cin>>choose
if(choose==2)
VsPlayer()
else if(choose==1)
VsComputer()
else if(choose==3)
exit(0)
else
{
cout<<"输入错误,请重新输入!"<<endl
system("pause")
}
}while(choose!=3)
return 0
}
void VsComputer()
{
bool ipjudge
CHESS newP
do
{
newP.coutChess()
newP.setStepC(ipjudge)//人机对战函数
if(!ipjudge)
continue
if(!newP.getTurn())
newP.flushChess()
newP.coutChess()
newP.judgeWin()
CHESS::count++
}while(newP.getAns()==0&&CHESS::count<256)
newP.winer()
}
void CHESS::setStepC(bool&ipjudge)
{
int i
if(flag)
{
cout<<"棋手走棋:"
cin>>inPut
for(i=0i<=1i++)
if(inPut[i]<'0'||(inPut[i]>'9'&&inPut[i]<'O')||(inPut[i]>'F'&&inPut[i]<'O')||inPut[i]>'f')
{
ipjudge=false
cout<<"输入越界,请重新输入!"
system("pause")
break
}
}
else
cSetStep()//轮到电脑走棋
}
void CHESS::cSetStep()
{
int i,j,depth=0
setPower()
for(i=0i<16i++)
{
for(j=0j<16j++)
{
if(CBoard[i][j]=='+')//优化:排除周围全是+的情况
{
if(CBoard[i-1][j]=='O'||CBoard[i-1][j-1]=='O'||CBoard[i][j-1]=='O'||CBoard[i+1][j-1]=='O'||CBoard[i+1][j]=='O'||CBoard[i+1][j+1]=='O'||CBoard[i][j+1]=='O'||CBoard[i-1][j+1]=='O')
{
judgePw(i,j,0,depth,'O')
judgePw(i,j,0,depth,'X')
}
else if(CBoard[i-1][j]=='X'||CBoard[i-1][j-1]=='X'||CBoard[i][j-1]=='X'||CBoard[i+1][j-1]=='X'||CBoard[i+1][j]=='X'||CBoard[i+1][j+1]=='X'||CBoard[i][j+1]=='X'||CBoard[i-1][j+1]=='X')
{
judgePw(i,j,0,depth,'O')
judgePw(i,j,0,depth,'X')
}
}
}
}
getFinalPw()
//coutPW()
//system("pause")
if(tPW[0]>0)
CBoard[tPW[1]][tPW[2]]='X'
/*else if(tPW[0]>0&&tPW[3]>1)
{
for(i=0i<16i++)
{
for(j=0j<16j++)
{
if(tPW[0]==PW[i][j])
if()
}
}
}*/
else
{
cout<<"权值函数错误!"
system("pause")
exit(1)
}
}
void CHESS::judgePw(int x,int y,int direction,int depth,char test)
{
if(depth>=0&&depth<4)
{
if(direction==1)//左方
{
if(CBoard[x-depth-1][y]==test)
judgePw(x,y,1,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==2)//左上方
{
if(CBoard[x-depth-1][y-depth-1]==test)
judgePw(x,y,2,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==3)//正上方
{
if(CBoard[x][y-depth-1]==test)
judgePw(x,y,3,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==4)//右上方
{
if(CBoard[x+depth+1][y-depth-1]==test)
judgePw(x,y,4,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==5)//右方
{
if(CBoard[x+depth+1][y]==test)
judgePw(x,y,5,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==6)//右下方
{
if(CBoard[x+depth+1][y+depth+1]==test)
judgePw(x,y,6,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==7)//正下方
{
if(CBoard[x][y+depth+1]==test)
judgePw(x,y,7,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==8)//左下方
{
if(CBoard[x-depth-1][y+depth+1]==test)
judgePw(x,y,6,depth+1,test)
else
PW[x][y]+=adddepth(depth)
}
else if(direction==0)
{
if(CBoard[x-depth-1][y]==test)//左方
judgePw(x,y,1,depth+1,test)
if(CBoard[x-depth-1][y-depth-1]==test)//左上方
judgePw(x,y,2,depth+1,test)
if(CBoard[x][y-depth-1]==test)//正上方
judgePw(x,y,3,depth+1,test)
if(CBoard[x+depth+1][y-depth-1]==test)//右上方
judgePw(x,y,4,depth+1,test)
if(CBoard[x+depth+1][y]==test)//右方
judgePw(x,y,5,depth+1,test)
if(CBoard[x+depth+1][y+depth+1]==test)//右下方
judgePw(x,y,6,depth+1,test)
if(CBoard[x][y+depth+1]==test)//正下方
judgePw(x,y,7,depth+1,test)
if(CBoard[x-depth-1][y+depth+1]==test)//左下方
judgePw(x,y,6,depth+1,test)
}
}
else if(depth==4)
PW[x][y]+=adddepth(depth)
else
{
cout<<"深度函数出错!"
system("pause")
exit(1)
}
}
int CHESS::adddepth(int depth)
{
switch(depth)
{
case 0:return 0break
case 1:return 1break
case 2:return 2break
case 3:return 4break
case 4:return 6break
default:
{
cout<<"深度函数错误!"
system("pause")
exit(1)
}
}
}
void CHESS::getFinalPw()
{
int i,j
for(i=0i<=15i++)
for(j=0j<=15j++)
{
if(PW[i][j]>tPW[0])
{
tPW[0]=PW[i][j]
tPW[1]=i
tPW[2]=j
tPW[3]=1
}
else if(PW[i][j]==tPW[0]&&PW[i][j]!=0)
tPW[3]+=1
}
}
////////////////////////////////////////////////////////////////////////
//双人对战
////////////////////////////////////////////////////////////////////////
void VsPlayer()
{
bool ipjudge
CHESS newP
do
{
newP.coutChess()
newP.setStep(ipjudge)
if(!ipjudge)
continue
newP.getTurn()
newP.flushChess()
newP.coutChess()
newP.judgeWin()
CHESS::count++
}while(newP.getAns()==0&&CHESS::count<256)
newP.winer()
}
void CHESS::winer()
{
if(CHESS::count==256)
{
cout<<"|||||||||平局!本局结束!"<<endl
system("pause")
exit(1)
}
if(result==1)
cout<<"|||||||||恭喜!棋手A赢得本局胜利!"<<endl<<endl
else if(result==2)
cout<<"|||||||||恭喜!棋手B赢得本局胜利!"<<endl<<endl
system("pause")
}
//默认构造函数
CHESS::CHESS()
{
int i,j
count=0
result=0
flag=true
for(i=0i<=15i++)
{
if(i<10)
CBoard[i][0]=i+48
else
CBoard[i][0]=i+55
}
for(i=0i<=15i++)
for(j=0j<=15j++)
CBoard[i][j]='+'
}
//填充权值函数
void CHESS::setPower()
{
int i,j
for(i=0i<=15i++)
for(j=0j<=15j++)
PW[i][j]=0
tPW[0]=0
tPW[1]=0
tPW[2]=0
tPW[3]=0
}
//输出棋盘函数
void CHESS::coutChess()
{
int i,j
system("cls")
cout<<" 欢乐五子棋"<<endl<<endl
cout<<" 0 1 2 3 4 5 6 7 8 9 A B C D E F"<<endl
for(i=0i<=15i++)
{
if(i>=0&&i<10)
cout<<i<<' '
else if(i>=10)
cout<<static_cast<char>(i+55)<<' '
else
cout<<"棋盘列序号输出错误!"
for(j=0j<=15j++)
cout<<CBoard[i][j]<<' '
cout<<endl
}
cout<<endl<<endl
}
//双人对战,棋手轮流走棋函数
void CHESS::setStep(bool&ipjudge)
{
if(flag)
cout<<"棋手A走棋:"
else
cout<<"棋手B走棋:"
cin>>inPut
for(int i=0i<=1i++)
{
if(inPut[i]<'0'||(inPut[i]>'9'&&inPut[i]<'O')||(inPut[i]>'F'&&inPut[i]<'O')||inPut[i]>'f')
{
ipjudge=false
cout<<"输入越界,请重新输入!"
system("pause")
break
}
}
}
//刷新棋盘
void CHESS::flushChess()
{
int i
temp[0]=inPut[0]
temp[1]=inPut[1]
for(i=0i<=1i++)
{
if(temp[i]>='0'&&temp[i]<='9')
num[i]=static_cast<int>(temp[i]-48)
else if(temp[i]>='O'&&temp[i]<='F')
num[i]=static_cast<int>(temp[i]-55)
else if(temp[i]>='O'&&temp[i]<='f')
num[i]=static_cast<int>(temp[i]-87)
else
{
cout<<"用户输入未知错误1,请重新输入!"
system("pause")
exit(1)
}
}
if(CBoard[num[0]][num[1]]=='+'&&!flag)
CBoard[num[0]][num[1]]='O'
else if(CBoard[num[0]][num[1]]=='+'&&flag)
CBoard[num[0]][num[1]]='X'
else
{
flag=!flag
cout<<"用户输入错误,请重新输入!"
system("pause")
}
}
//判断胜出新算法
void CHESS::judgeWin()
{
int i=0,j
do
{
j=0
do
{
if(CBoard[i][j]=='O')
result=judgeAWin(i,j)
else if(CBoard[i][j]=='X')
result=judgeBWin(i,j)
else if(CBoard[i][j]=='+')
else
{
cout<<"棋盘["<<i<<"]["<<j<<"]位置信息错误!"<<endl
system("pause")
exit(1)
}
if(result==1||result==2)
break
j++
}while(j<=15)
if(result==1||result==2)
break
i++
}while(i<=15)
}
//对棋手A的棋子检测
int CHESS::judgeAWin(int a,int b)
{
if(CBoard[a][b-1]=='O'&&CBoard[a][b-2]=='O'&&CBoard[a][b+1]=='O'&&CBoard[a][b+2]=='O') //横向五子
return 1
else if(CBoard[a+1][b]=='O'&&CBoard[a+2][b]=='O'&&CBoard[a-1][b]=='O'&&CBoard[a-2][b]=='O') //纵向五子
return 1
else if(CBoard[a+1][b+1]=='O'&&CBoard[a+2][b+2]=='O'&&CBoard[a-1][b-1]=='O'&&CBoard[a-2][b-2]=='O') //左上右下五子
{
return 1
}
else if(CBoard[a+1][b-1]=='O'&&CBoard[a+2][b-2]=='O'&&CBoard[a-1][b+1]=='O'&&CBoard[a-2][b+2]=='O') //左下右上五子
return 1
else
return 0
}
//对棋手B的棋子检测
int CHESS::judgeBWin(int a,int b)
{
if(CBoard[a][b-1]=='X'&&CBoard[a][b-2]=='X'&&CBoard[a][b+1]=='X'&&CBoard[a][b+2]=='X') //横向五子
return 2
else if(CBoard[a+1][b]=='X'&&CBoard[a+2][b]=='X'&&CBoard[a-1][b]=='X'&&CBoard[a-2][b]=='X') //纵向五子
return 2
else if(CBoard[a+1][b+1]=='X'&&CBoard[a+2][b+2]=='X'&&CBoard[a-1][b-1]=='X'&&CBoard[a-2][b-2]=='X') //左上右下五子
return 2
else if(CBoard[a+1][b-1]=='X'&&CBoard[a+2][b-2]=='X'&&CBoard[a-1][b+1]=='X'&&CBoard[a-2][b+2]=='X') //左下右上五子
return 2
else
return 0
}
//输出权值表
void CHESS::coutPW()
{
int i,j
system("cls")
cout<<" 欢乐五子棋(权值表)"<<endl<<endl
cout<<" 0 1 2 3 4 5 6 7 8 9 A B C D E F"<<endl
for(i=0i<=15i++)
{
if(i>=0&&i<10)
cout<<i<<' '
else if(i>=10)
cout<<static_cast<char>(i+55)<<' '
else
cout<<"棋盘列序号输出错误!"
for(j=0j<=15j++)
cout<<PW[i][j]<<' '
cout<<endl
}
cout<<endl<<endl
}
任何一种棋类游戏其关键是对当前棋局是否有正确的评分,评分越准确则电脑的AI越高。五子棋游戏也是如此,但在打分之前,我们先扫描整个棋盘,把每个空位从八个方向上的棋型填入数组gStyle(2, 15, 15, 8, 2),其中第一个下标为1时表示黑棋,为2时表示白棋,第二和第三
个下标表示(x,y),第四个下标表示8个方向,最后一个下标为1时表示棋子数,为2时表示空格数,如:gStyle(1,2,2,1,1)=3表示与坐标(2,2)在第1个方向上相邻的黑棋棋子数为3
gstyle(1,2,2,1,2)=4表示与坐标(2,2)在第1个方向上的最近的空格数为4
在定义方向时,也应该注意一定的技巧,表示两个相反的方向的数应该差4,在程序中我是这样定义的:
Const DIR_UP = 1
Const DIR_UPRIGHT = 2
Const DIR_RIGHT = 3
Const DIR_RIGHTDOWN = 4
Const DIR_DOWN = 5
Const DIR_DOWNLEFT = 6
Const DIR_LEFT = 7
Const DIR_LEFTUP = 8
这样我们前四个方向可以通过加四得到另一个方向的值。如果你还是不太明白,请看下面的图:
---------
---------
---oo----
-ox*xx---
---------
---------
图中的*点从标为(4,4),(打*的位置是空位),则:
gStyle(2,4,4,1,1)=1在(4,4)点相邻的上方白棋数为1
gStyle(2,4,4,1,2)=2在(4,4)点的上方距上方白棋最近的空格数为2
gStyle(1,4,4,3,1)=2在(4,4)点相邻的右方黑棋数为2
gStyle(1,4,4,3,2)=1在(4,4)点的右方距右方黑棋最近的空格数为3
...一旦把所有空点的棋型值填完,我们很容易地得出黑棋水平方向上点(4,4)的价值,由一个冲1(我把有界的棋称为冲)和活2(两边无界的
棋称为活)组成的。对于而白棋在垂直方向上点(4,4)的价值是一个活1,而在/方向也是活1所以,只要我们把该点的对于黑棋和白棋的价值算出
来,然后我们就取棋盘上各个空点的这两个值的和的最大一点作为下棋的点。然而,对各种棋型应该取什么值呢?我们可以先作如下假设:
Fn 表示先手n个棋子的活棋型,如:F4表示先手活四
Fn'表示先手n个棋子的冲棋型,如:F4'表示先手冲四
Ln 表示后手n个棋子的活棋型,如:L3表示后手活三
Ln'表示后手n个棋子的冲棋型,如:L3'表示后手冲三
.
.
.
根据在一行中的棋型分析,得到如下关系:
L1'<=F1'<L2'<=F2'<=L1<F1<L2<F2<L3'<=F3'<L4'<F4'=F4
从这个关系包含了进攻和防守的关系(当然,这个关系是由我定的,你可以自己定义这些关系)。对这些关系再进一步细化,如在一个可下
棋的点,其四个方向上都有活三,也比不上一个冲四,所以我们可以又得到4*F3<L4'这个关系,同样,我们还可以得到其它的关系,如:4*F2<L3、4*L3<F3...,这些的关系由于你的定法和我的定法制可能不一样,这样计算机的AI也就不一样,最后我们把分值最小的L1'值定为1,则我们就得
到了下面各种棋型的分值,由C语言表示为:
F[2][5]={{0,2,5,50,16000},{0,10,30,750,16000}}
L[2][5]={{0,1,5,50,3750},{0,10,30,150,4000}}
F数组表示先手,第一个下标为0时表示冲型,第二个下标表示棋子数,则F2'对应F[0][2]L数组表示后手,第一个下标为0时表示冲型,第二
个下标表示棋子数,则L2对应F[1][2]Ok,棋型的分值关系确定好了以后,我们把每一个可下点的四个方向的棋型值相加(包括先手和后手的分
值),最后选择一个最大值,并把这一点作为计算机要下的点就OK了:)。后话:
1、得到最大值也许不止一个点,但在我的程序中只选择第一个最大点,当然你可以用于个随机数来决定
选择那一个最大值点,也可以对这些最大值点再作进一步的分析。
2、在这个算法中我只考虑了周围有棋子的点,而其它点我没有考虑。
3、可以再更进一步,用这个算法来预测以后的几步棋,再选择预测值最好的一步,这样电脑的AI就更高了
4、这个算法没有考虑黑棋的禁手(双3、双四和多于五子的连棋)。因为在平时我下的五子棋是没有这些
禁手的。