一. 项目背景
1. 项目任务
编写人机对弈的五子棋游戏
2. 项目功能
1) 人机交互下子
2) 电脑有一定的防御能力
3) 用户可以任意终止游戏或重新开始
4) 可以判断双方的输赢
3. 运行条件
Windows 98/XP
Visual C++ 6.0
二. 项目实现
1. 算法
1)要完成该游戏首先要解决显示问题,因此定义一棋盘类。该类中应包括棋盘信息,判断
输赢的函数,电脑完成计算的函数,以及游戏终止的函数。定义的棋盘类如下:
class Qipan
{public:
char Qizi[9][9];//棋盘信息
char C_Weizi[2];//存放电脑要下的棋子坐标
void QipanInitialize();//对棋盘初始化
~Qipan();
void Qipan::ShowMe();//显示棋盘
bool Qipan::Check();//判断棋盘是否已下满
void C_Qizi(int i0,int j0);//电脑计算出要下的坐标
bool Win_lose(char a,int i0,int j0);//判断输赢
};
用Qizi[9][9]来存放各位置的棋子信息(未下的为*,电脑的为1,用户的为0),用户输入要下棋子坐标
后,电脑根据用户刚下的位置计算自己要下的位置,用伪代码描述为:
1) 获得用户刚下的位置坐标i0,j0
2) 在I0,J0所在的行和列分别查找用户的棋子个数sum_line,sum_row
3) if(sum_row>=sum_line)
{
从I0,J0开始向左查找相连的子;
if(没有空位)
{再从I0,J0向又查找;
if(没有空位)
{ 从该位置向右查找空位;
保存该位置到C_Weizi[2];
}
else
{保存该位置到C_Weizi[2];}
}
else
{保存该位置到C_Weizi[2];}
}
else
{
仿造上述方法在列中查找;
找到后保存该位置到C_Weizi[2];
}
电脑根据计算所得的坐标下子。
2)主函数的流程:
初始化棋盘;
显示棋盘;
while(棋盘未下满)
{ 用户输入要下的位置i0,j0;
根据i0,j0的值执行相应的功能;
判断是否赢了;
if(赢)
{break;}
else
{电脑计算并下子;
判断是否赢了;
}
2. 程序
#include <iostream.h>
//定义棋盘类
class Qipan
{public:
char Qizi[9][9];//棋盘信息
char C_Weizi[2];//存放电脑要下的棋子坐标
void QipanInitialize();//对棋盘初始化
~Qipan();
void Qipan::ShowMe();//显示棋盘
bool Qipan::Check();//判断棋盘是否已下满
void C_Qizi(int i0,int j0);//电脑计算出要下的坐标
bool Win_lose(char a,int i0,int j0);//判断输赢
};
//定义棋盘初始化函数
void Qipan::QipanInitialize()
{
for(int i=0;i<=8;i++)
{for(int j=0;j<=8;j++)
Qizi[i][j]='*';
}
}
//定义解构函数
Qipan::~Qipan()
{cout<<endl<<"欢迎下次再玩!"<<endl<<endl;}
//定义显示函数
void Qipan::ShowMe()
{
for(int i=0;i<=8;i++)
{
switch(i)
{
case 2:
{
for(int j=0;j<=8;j++)
{ if(j==8)
cout<<Qizi[i][j]<<" "<<"SIGO五子棋说明:"<<endl<<endl;
else
cout<<Qizi[i][j]<<" ";
}
break;
}
case 3:
{
for(int j=0;j<=8;j++)
{ if(j==8)
cout<<Qizi[i][j]<<" "<<"1)你的棋子为0,电脑的棋子为1"<<endl<<endl;
else
cout<<Qizi[i][j]<<" ";
}
break;
}
case 4:
{
for(int j=0;j<=8;j++)
{ if(j==8)
cout<<Qizi[i][j]<<" "<<"2)你只须键入你要下的棋子坐标"<<endl<<endl;
else
cout<<Qizi[i][j]<<" ";
}
break;
}
case 5:
{
for(int j=0;j<=8;j++)
{ if(j==8)
cout<<Qizi[i][j]<<" "<<"3)重新开始请输入-1,-1"<<endl<<endl;
else
cout<<Qizi[i][j]<<" ";
}
break;
}
case 6:
{
for(int j=0;j<=8;j++)
{ if(j==8)
cout<<Qizi[i][j]<<" "<<"4)结束游戏请输入0,0"<<endl<<endl;
else
cout<<Qizi[i][j]<<" ";
}
break;
}
default:
{
for(int j=0;j<=8;j++)
{ if(j==8)
cout<<Qizi[i][j]<<endl<<endl;
else
cout<<Qizi[i][j]<<" ";
}
}
}
}
}
//判断是否棋盘已下满
bool Qipan::Check()
{
for(int i=0;i<=8;i++)
{
for(int j=0;j<=8;j++)
{
if(Qizi[i][j]=='*')
return true;
}
}
return false;
}
//电脑计算要下的子坐标并保存
void Qipan::C_Qizi(int i0,int j0)
{
int sum_line=0,sum_row=0;
for(int i=0;i<=8;i++)
{if(Qizi[i][j0]=='0')
sum_line++;
}
for(int j=0;j<=8;j++)
{if(Qizi[i0][j]=='0')
sum_row++;
} //比较横行和纵行上人的棋子个数
if(sum_row>=sum_line)
{
j=j0;
while(Qizi[i0][j]=='0'&&j>=0)
j--;
if(j<0||Qizi[i0][j]=='1')
{
j=j0;
while(Qizi[i0][j]=='0'&&j<=8)
j++;
if(Qizi[i0][j]=='1')
{
j=j0;
while((Qizi[i0][j]=='1'||Qizi[i0][j]=='0')&&j<=8)
j++;
C_Weizi[0]=i0;//存放要下的位置坐标
C_Weizi[1]=j;
}
else
{C_Weizi[0]=i0;//存放要下的位置坐标
C_Weizi[1]=j;
}
}
else
{C_Weizi[0]=i0;//存放要下的位置坐标
C_Weizi[1]=j;
}
}
//如果纵行人的棋子较多
else
{//从人刚下的位置往上检验相连的子
i=i0;
while(Qizi[i][j0]=='0'&&i>=0)
i--;
if(i<0||Qizi[i][j0]=='1')
{ i=i0;
while(Qizi[i][j0]=='0'&&i<=8)
i++;
if(Qizi[i][j0]=='1')
{
while((Qizi[i][j0]=='0'||Qizi[i][j0]=='1')&&i<=8)
i++;
C_Weizi[0]=i;
C_Weizi[1]=j0;
}
else
{C_Weizi[0]=i;
C_Weizi[1]=j0;
}
}
else
{ C_Weizi[0]=i;
C_Weizi[1]=j0;
}
}
//判断输赢(可以对人和电脑判断)
bool Qipan::Win_lose(char a,int i0,int j0)
{ int i=i0-1;int j=j0-1;
int sum_line=0,sum_row=0,sum_xie1=0,sum_xie2=0;
while(Qizi[i][j0]==a&&i>=0)
i--;
i+=1;
while(Qizi[i][j0]==a)
{
sum_line++;
i++;
}
if(sum_line==5) return true;//判断纵行是否满足
while(Qizi[i0][j]==a&&j>=0)
j--;
j+=1;
while(Qizi[i0][j]==a)
{sum_row++;
j++;
}
if(sum_row==5) return true;//判断横行是否满足
i=i0-1;j=j0-1;
while(Qizi[i][j]==a&&i>=0&&j>=0)
{i--;j--;}
i+=1;
j+=1;
while(Qizi[i][j]==a)
{sum_xie1++;i++;j++;}
if(sum_xie1==5) return true;//判断斜行是否满足
i=i0-1;j=j0+1;
while(Qizi[i][j]==a&&i>=0&&j>=0)
{i--;j++;}
i+=1;j-=1;
while(Qizi[i][j]==a)
{sum_xie2++;i++;j--;}
if(sum_xie2==5) return true;//判断另一斜行是否满足
return false;
}
//主函数
void main()
{
Qipan qipan1;
qipan1.QipanInitialize();//初始化棋盘
int i=1,j=1;
while(qipan1.Check())//当棋盘全部下满时停止游戏
{
qipan1.ShowMe();
if(qipan1.Win_lose('1',i-1,j-1))//判断电脑是否赢了
{
cout<<"你输了!!"<<endl<<endl;
break;
}
else
{
cout<<"输入棋子位置:";
cin>>i>>j;
if(i==0&&j==0)
{break;}
else
{
if(i==-1&&j==-1)
{
qipan1.QipanInitialize();
qipan1.ShowMe();
cout<<"输入棋子位置:";//刷新之后重新开始
cin>>i>>j;
qipan1.Qizi[i-1][j-1]='0';
if(qipan1.Win_lose('0',i-1,j-1))
{
cout<<"你赢了!!"<<endl<<endl;
break;
}
else
{//电脑完成计算
qipan1.C_Qizi(i-1,j-1);
qipan1.Qizi[qipan1.C_Weizi[0]][qipan1.C_Weizi[1]]='1';
}
}
else
{ //输入的坐标必须满足条件
while(i<=0||i>9||j<=0||j>9||qipan1.Qizi[i-1][j-1]=='0'||
qipan1.Qizi[i-1][j-1]=='1')
{
cout<<"请重新输入:";
cin>>i>>j;
}
qipan1.Qizi[i-1][j-1]='0';
//判断人是否赢了
if(qipan1.Win_lose('0',i-1,j-1))
{
cout<<"你赢了!!"<<endl<<endl;
break;
}
else
{//电脑完成计算
qipan1.C_Qizi(i-1,j-1);
qipan1.Qizi[qipan1.C_Weizi[0]][qipan1.C_Weizi[1]]='1';
}
}
}
}
}
}
三. 项目总结
1) 编写类的多个成员函数时应该逐个编写并检验,否则出错的话很难查找。
2) 一个程序的关键部分是算法,让电脑做一件就要为它编制相应的算法。
3) 编写程序时应该考虑到所有的可能性,并给出相应的处理方法。
4) 逐步求精。例如当编写电脑计算如何下子的函数时,先实现一些比较简单的功能,让它可以给出一个
没有下过子的位置,然后检验该函数,成功的话,就说明该函数的接口是可行的,然后只须再改进该、
函数即可,增强它的功能。