VC++VC++VC++课程设计课程设计课程设计
VC++课程设计报告
课
院
程:
系:
选题名称: TICTACTOE 游戏(三子连珠游戏)
题目级别: B 级
班
学
姓
时
级:
号:
名:
间:
指导老师:
(源码网整理:www.codepub.com)
0
VC++VC++VC++课程设计课程设计课程设计
程序功能简介
该程序是 TICTACTOE 游戏,棋盘为 3*3,3 个棋子连起来即为赢。输入参数
时,若输入的数字超过边界或在已有棋子的地方再输入,则提示出错,要求重
新输入。
课程设计要求
1. 将程序改写成类。
2. 将棋盘扩充成 4*4。
3. 统计输赢结果并打印输出。
4. 选做:使游戏更生动。(输赢条件相同)
评定难易程度:B
程序设计思想
设计一个玩家下棋的操作类,里面封装了棋盘﹑棋子类别和输赢次数等成员
数据。
操作类的函数包括有初始化棋盘﹑下棋﹑判断输赢等。定义一个类的对象,
在调用类中函数时直接使用对象名加函数名即可。
将两个玩家定义为两个数组用于区分他们的信息,分别用‘O’和‘X’代表
两个玩家所用棋子,然后分别对两个玩家通过已定义的类的对象调用类中的函
数,完成下棋过程。
进一步改进程序:
1. 程序中用 if
else if 格式的语句来判断玩家的输赢,当棋盘增大时,三
子连珠的各种可能性成倍增加,使得语句非常繁琐,所以将其改为 for 语
句的循环结构进行判断,当棋盘增大时,只需更改循环条件结束的参数即
可,可灵活使用于各种尺寸的棋盘。
2. 为防止用户误操作,特别是在姓名的输入时的失误,可以采用 cin.getline
函数读入在判断确认。
3. 在完成第一步的基础上可将三子连珠扩充为标准的五子棋。
原程序各个功能说明
原程序的结构(见下页)(源码网整理:www.codepub.com)
1
VC++VC++VC++课程设计课程设计课程设计
错
误
错
误
不
符
合
Y
结束
N
开始
输 出 菜 单 及 提 示
信息
输入姓名
由“O”先手下棋
“O”输入棋子所在
的行列
正
确
“X”输入棋子所在
的行列
正确
判 断 胜 负 ﹑ 和
棋条件
符
合
输出结果
询问是否继续下棋
输出最终结果
2
VC++VC++VC++课程设计课程设计课程设计
1. 类的说明
类是对一组具有相同属性、相同行为的对象的抽象的描述,不占用内存空间。
可以把类看作“理论上”的对象,也就是说,它为对象提供蓝图,但在内存中并
不存在。从这个蓝图可以创建任何数量的对象。从累创建的所有对象都有相同的
成员:属性、行为或方法。这些对象才是具体的独立的个体,占用存储空间,因
此,对象又称作类的实例。
下面以具体程序说明。
在头文件之后定义了一个名为 CGobang 的类:
class CGobang
{
char board[4][4];//用数组来定义一个 4*4 的棋盘
public:
void Guize();//打印菜单总体情况,说明游戏规则和方法,无返回值
void Dayinqp();//打印棋盘,无返回值
void Xiaqi(char*,int &,int &,char);//下棋,无返回值
int Huosheng(int,int,char);//判断那个选手赢,返回值为一整形数
void Choice(char &);//是否再玩,无返回值
void Chongzhi();//重置棋盘,无返回值
};
在 这 个 类 中 , 定 义 了 一 个 未 经 说 明 的 数 组 被 默 认 为 私 有 成 员 , 即 char
board[4][4],用它来存储玩家在下棋的过程中所输入的棋子的坐标,并以此来
区分表示不同的两个玩家。另外,这个类中还说明了五个没有返回值的函数以及
一个返回值为整形数的函数,对它们的定义将出现在主函数完成之后,所以本着
“先定义,后调用”的原则,在类中的说明相当于自定义函数中的函数说明,这
样可以使在主函数调用类中的公有函数时变得更加明了清晰。
在这个函数中,对类的对象的定义出现在了主函数第一行,即:
CGobang s;//定义类的一个对象 s
对这个对象的定义的目的是,在下面不同玩家的下其中的不同操作中,可以直接
通过这个对象来分别调用类中的任意公有成员函数,这样就避免了通过类名来调
用函数时可能出现的冗长。可以说,在这个程序里,这个对象 s 起到了把主函数
与类连接起来的桥梁作用。
课程设计要求的实现
一、类的封装。
首先,在源程序代码中出现的主要函数程序有:
void PrintBoard();
void PrintInfo();
void PrintStats(int, int, int);
void IfError(int&, int&);
void ChoiceOfChar(char&);
void PromptTurnO(int&, int&);
void PromptTurnX(int&, int&);
char board[3][3];
// 打印棋盘
//输出游戏提示信息
// 输出统计的胜负信息
// 判断输入时候有错
// 询问用户是否在玩一局
// 选择“O”的玩家下棋(先走)
// 选择“X”的玩家下棋(后走)
// 棋盘
3
VC++VC++VC++课程设计课程设计课程设计
在我的程序中的主要函数程序为:
void Guize();//打印菜单总体情况,说明游戏规则和方法,无返回值
void Dayinqp();//打印棋盘,无返回值
void Xiaqi(char*,int &,int &,char);//下棋,无返回值
int Huosheng(int,int,char);//判断那个选手赢,返回值为一整形数
void Choice(char &);//是否再玩,无返回值
void Chongzhi();//重置棋盘,无返回值
以此来对比说明。
◎源代码中的关于打印棋盘以及规则提示的说明的函数没有变动。
◎去掉了关于输出统计胜负信息的函数,在主函数体中用三个一组的条件自
增变量代替,这样减少了复杂的函数调用,说明以及定义,精简了程序,并且达
到了预想的目的。
◎由于对玩家输入是否有错的判断在玩家每次确定下棋之后必须进行,所以
如果像源代码一样把玩家下棋的函数和判断是否有误分开定义的话,在没下一步
棋之后都要重复调用判断函数,显得十分复杂,所以把源代码中的判断是否有错
的函数与玩家下棋合并在
void Xiaqi(char*,int &,int
&,char)
中,对它们的定义在后面的类的成员函数的定义中一起定义,这样可以减少很多
不必要的麻烦,起到了精简程序的作用。
◎源代码中把连个玩家分别定义为两个函数,用以区别先后手,然后在定义
一个数组 char board[3][3](由于源代码中显示的是 3*3 的棋盘,所以数组定
义为 board[3][3])来表示棋盘。在这点我做了比较大的改动,首先是定义了一
个类中的私有成员数组,用以记录每位玩家所下棋子的坐标,这样既可以通过用
不同的指针变量来指向键盘的方法来区分不同的玩家,亦可以同时用它表示整个
棋盘。这样使程序得到了进一步的精简。
void CGobang::Xiaqi(char*player1,int & x,int & y,char c)
{
cout<<"该"<
>x;
cout<<" "<<" "<<" "<<" "<<"棋子所在列:";
cin>>y;
while(x>4||y>4||x<1||y<1||‘ ’!=board[x-1][y-1])
//输入数字超过边界或在已有棋子的地方再输入
//' '!=board[x-1][y-1]表示已有棋子
{
cout<<"对不起,您的输入有误,请重新输入!"<>x;
cout<<" "<<" "<<" "<<" "<<"棋子所在列:";
cin>>y;
}
board[x-1][y-1]=c;//在 x 行,y 列处下 O 或 X
}
4
◎ 由 于 需 要 重 复 使 用 空 棋 盘 , 我 又 增 加 了 一 个 重 置 棋 盘 的 函 数 void
VC++VC++VC++课程设计课程设计课程设计
Chongzhi()来进行每次开始下棋时的棋盘清空重置。
void CGobang::Chongzhi()
{
for(int Row=0;Row<4;Row++)
for(int Column=0;Column<4;Column++)
board[Row][Column]=' ';
}
◎完成对主要函数的改动之后,把它们封装到类 CGobang 中即可。
二、将棋盘扩充为 4*4
在类的封装中已经详细的将我改动过的棋盘说明了,所以,要把棋盘扩充为
n*n 只需把定义过的私有成员数组 board[n][n]中的 n 改为所需数字即可。即,
定义 board[4][4],就完成了对棋盘 4*4 的扩充。
三、统计输出结果并打印输出。
同样,在类的封装中也已说明我对记录胜负结果以及输出的方法,下面给出
源代码:
记录胜负结果:
int FirstWin=0,SecondWin=0,Draws=0,x,y,N;
在每次获胜、失败以及和棋的判断完成之后,令 FirstWin SecondWin
Draws 分别依情况自增,达到记录胜负次数的目的。
打印输出:
//输出游戏输赢次数
cout<
VC++VC++VC++课程设计课程设计课程设计
cin.getline(player1,20);//连续读取数据,实现对玩家一命名
cout<<"请输入第二个玩家姓名:";
cin.getline(player2,20);//连续读取数据,实现对玩家二命名
while(choice=='Y'||choice=='y')//条件成立,开始下棋
{
s.Chongzhi();//通过对象 s 调用 Chongzhi 函数
N=0;//对变量付初值,表示已经下在棋盘上的妻子总数
while(N<=16)//在棋盘未满的条件下下棋
{
s.Dayinqp();//打印棋盘
s.Xiaqi(player1,x,y,'O');//调用下棋的运行函数
N++;//记录已下棋子数
if(s.Huosheng(x-1,y-1,'O'))//返回值不为 0 则条件成立
{
cout<
VC++VC++VC++课程设计课程设计课程设计
//定义判断获胜条件的函数
/*此函数以下棋的点为坐标分别向 8 个方向判断是否能构成胜利条件,若能
返回值 1,则该选手获胜,程序结束;若不能,返回值为 0,继续循环下棋程
序。*/
int CGobang::Huosheng(int x,int y,char c)
{
int count,a;
//从 左斜上 方向判断是否构成胜利条件
for(a=0,count=0;(x-a>=0)&&(y-a>=0)&&(board[x-a][y-a]==c);a++)
if(++count==3) return 1;
//从 右斜下 方向判断是否构成胜利条件
for(a=1;(x+a<4)&&(y+a<4)&&(board[x+a][y+a]==c);a++)
if(++count==3) return 1;
//从 上向下 方向判断是否构成胜利条件
for(a=0,count=0;(x-a>=0)&&(board[x-a][y]==c);a++)
if(++count==3) return 1;
//从 下向上 方向判断是否构成胜利条件
for(a=1;(x+a<4)&&(board[x+a][y]==c);a++)
if(++count==3) return 1;
//从 左至右 方向判断是否构成胜利条件
for(a=0,count=0;(y-a>=0)&&(board[x][y-a]==c);a++)
if(++count==3) return 1;
//从 右至左 方向判断是否构成胜利条件
for(a=1;(y+a<4)&&(board[x][y+a]==c);a++)
if(++count==3) return 1;
//从 右斜上 方向判断是否构成胜利条件
for(a=0,count=0;(x-a>=0)&&(y+a<4)&&(board[x-a][y+a]==c);a++)
if(++count==3) return 1;
//从 左斜下
for(a=1;(x+a<4)&&(y-a>=0)&&(board[x+a][y-a]==c);a++)
方向判断是否构成胜利条件
if(++count==3) return 1;
return 0;
}
设计过程中的疑难及解答方案和设计的不足
◎设计过程中的疑难及解答方案
在这次课程设计的过程中,我遇到了不小的阻力,还有也从中学习到了很多
以前没有掌握好或这根本没有掌握的知识。
在 定 义 玩 家 名 字 的 输 入 接 受 的 时 候 我 遇 到 了 一 个 从 未 见 过 的 函 数
cin.ignore(),这个函数是我从一个同样是输入存储用户姓名的程序里看到
的,上面对它的注释是:“输入输出流,前面如果有输入把输入行所有字符去
空,一边后面的输入从新的一行开始。”我对这个想了很长时间,但是一直没
有明白。最后解决问题的方法是我通过网络查询找到了这个函数,在其他一些
7