电气信息学院
实训报告
课 程 名 称: 软件项目实训
实 训 题 目: 猜数字游戏
年级/专业/班: 20##过程自动化01
学 生 姓 名:
学 号:
实训成绩:
指导教师签名: 年 月 日
1 引言
1.1系统概述
猜数字是一种益智类小游戏,游戏规则比较简单,一般两个人玩,一方出数字,一方猜。要求用VC++6.0中MFC编写一个程序实现猜数字游戏中的出数字方,实现数字之间的相互比较,让玩家找出电脑写的三位数字,而玩家在找出三位数字中,电脑需要给出玩家一些提示信息,用以帮助玩家找出答案。对于猜一个各个位数不等的三位数字,电脑需要在程序刚运行时,确定一个随机的三位数,且各个位数不相等。而在玩家输入数字时,也需要检验玩家输入的数字是否满足条件,即一个各个位数不重复的三位数。只有玩家输入正确的数字后,电脑才能进行比较数字的运算,如果玩家输入的数字和电脑的不相等,输出提示信息,并应重新读取玩家的数字进行判断,直到玩家放弃猜数字或数字猜对为止。玩游戏者通过游戏提示输入八次来匹配上面所输入的数字。A表示位置正确且数字正确,B表示数字正确而位置不正确。
首先,由设计程序随机产生一个三位整数k,要求这个两位数字没有重复数字,且最高位不是0。然后,玩家可以猜一个三位数字,根据玩家猜的数,与k比较,判断玩家猜的数字是否正确,同时,提示猜的数字中位置正确的数字个数m和数字正确而位置不正确的数的个数n,并以mAnB的形式输出(A表示位置和数字都正确,B表示数字正确而位置不正确)。如果玩家猜了9次还不对,那么提示玩家“游戏失败!”。
1.2 需求分析
电脑游戏的出现与1960年代电子计算机进入美国大学校园有密切的联系。当时的环境培养出了一批编程高手。1962年一位叫斯蒂夫·拉塞尔的大学生在美国DEC公司生产的PDP-1型电子计算机上编制的《宇宙战争》(Space War)是当时很有名的电脑游戏。一般认为,他是电脑游戏的发明人。 1970年代,随着电子计算机技术的发展,其成本越来越低。1971年,誉为“电子游戏之父”的诺兰·布什内尔发明了第一台商业化电子游戏机,不久他创办了世界上第一家电子游戏公司——雅达利公司(ATARI)。在1970年代,随着苹果电脑的问世,电脑游戏才真正开始了商业化的道路。此时,电脑游戏的图形效果还非常简陋,但是游戏的类型化已经开始出现了。从1980年代开始,PC机大行其道,多媒体技术也开始成熟,电脑游戏则成为了这些技术进步的先行者。尤其是3Dfx公司的3D显示卡给行业带来了一场图像革命以后。进入1990年代,电脑软硬件技术的进步,因特网的广泛使用为电脑游戏的发展带来了强大的动力。进入21世纪,网络游戏成为了电脑游戏的一个新的发展方向。
受到我们传统教育思想的困扰,我们习惯的认为游戏就是玩物丧志的东西,所以在我们那一代,父母只要听到游戏两个字那就像见了老虎一样的警惕和恐惧。由于这种传统思想的影响,当我国游戏事业高速发展时,才发现游戏设计方面人才存在着巨大的缺口和不足,再加上我国高校和传统学校对游戏设计专业的漠视,让游戏设计人才几乎难觅踪迹。直到现在,游戏设计专业已经超过了许多专业成为当下就业最热门的专业之一。而这种热门情况将随着人才缺口的扩大而继续持续。所以游戏设计专业的就业前景在未来十年都将是热门行业,同时也希望我们家长能够摒弃过去传统思想的干扰,正确认识游戏设计专业,让更多对游戏设计专业有兴趣的青少年选择自己的爱好。
网络游戏是一种新颖的休闲娱乐方式,其虚拟性、互动性是其它娱乐方式所无法比拟的。网络游戏同时具有开发青少年智力,培养、提高创新意识,磨练意志品质,激发自信心,加强团队配合精神等方面具有积极的意义。而在学习压力越来越大、代沟冲突也日趋激烈的今天,青少年需要找到一个缓解情绪的途径,网络游戏营造的虚空间恰恰满足了这种需求。随时就势、潜移默化,"润物细无声",寓民族传统文化教育于网络游戏,在游戏的游戏性、知识性、文化性和市场性中寻找一个平衡点。网络游戏设计的发展不仅仅是设计师的个人问题而是设计师与大众的配合的问题,寻找相同的切入点一一传统文化的介入,对未成年人的成长、传统文化自身的发展和改良游戏文化环境都具有推动作用。当我们努力在对游戏内的一些现象和功能进行研究的时候,我们却忽视了一个本体,这个本体是我们进行相关研究的基础,他就是“游戏”这一概念的本身。什么是游戏,游戏是什么,它具有怎么样的特征?它具有哪些作用?游戏仅仅如大家所认为那样只是单纯的娱乐活动,还是一种文化或者行为的现象,我们作为游戏设计的从业人员,只有清楚了解这些的时候,才能真正认清我们的行业,扩展游戏市场的发展空间,而不至于在一个狭隘的道路中追求可怜而有限的资源。因此,对我们过程自动化的学生来说,学习并掌握一些简单的电脑游戏编程是非常有必要的。
本次课题是猜数字游戏的设计与实现。主要是通过C++语言,实现猜数字游戏中的出数字方,在VC++6.0环境中实现由电脑随机产生一个数字不重复的三位数(最高位不为零),并将所猜的数与它自动产生的数进行比较,若猜得不对并作出提示:"mAnB",A代表位置正确数字也正确,B代表数字正确但位置不正确。m代表A的个数,n代表B的个数.在每次输入数字后,显示相应的提示信息,直到玩家猜对为止。
1.3 运行环境
Windows 7 系统
2 概要设计
2.1 设计思路
该游戏的主体功能主要有以下几个重要的模块:电脑产生随机数模块、玩家阅读游戏规则模块、电脑对玩家所猜数字进行处理模块以及电脑处理玩家退出游戏的模块。其中电脑产生随机数模块是在玩家开始玩游戏时电脑事先确定好三位不同数字,并作为正确数字供玩家猜测;对玩家数字所猜测的数字进行处理模块主要就是把玩家所猜测的数字与正确答案做个比较,然后为玩家提供相应的提示信息,以帮助玩家顺利猜出正确答案;玩家阅读游戏规则模块就是在玩家玩猜数游戏之前,了解猜数游戏的相关规则;退出模块主要是玩家想放弃游戏时做游戏作出处理并与此同时告诉玩家答案。
2.2 模块功能介绍
玩家开始进入游戏,有三个菜单选项:启动游戏,新游戏和帮助。启动游戏是帮助玩家熟悉猜数字游戏的规则,有关是显示猜数字游戏的一些版本号信息,而新游戏则是猜数字游戏的主体部分。首先由电脑产生三位不重复的数字供玩家猜测,接着游戏玩家输入两位不同数字,然后程序会对这组数字与正确答案做出比较并告知玩家相应的提示信息。图中共有5种不同的提示信息,“A”表示位置和数字都猜对了,“B”表示位置不对而数字正确。提示信息显示为“3A0B”,就表示玩家游戏成功。如果玩家在猜测了9次之后,仍然没有猜出正确答案,那么游戏失败。
2.3 模块结构图
2.4 程序流程图
2.5 功能设计分工
共有产生随机数功能模块,接受游戏规则的帮助模块和开始游戏模块三个模块,有我们两人共同设计实验完成
3 详细设计
3.1 产生随机数模块
C语言中的随机数也是伪随机数,即是由小M多项式序列生成的,其中产生每个小序列都有一个初始值,即随机种子。伪随机数是相对于真随机而言的,电脑通过发送种子数值,运算算法产生缪个看起来像随机数的数字,但是实际上这个数字是可以预测的,这是由于电脑没有从环境中收集到任何随机信息。rand()函数的内部实现是用线性同余法做的,它不是真的随机数,因其周期特别长,故在一定的范围里可看成是随机的。使用rand()函数获取一定范围内的一个随机数,如果想要获取在一定范围内的数的话,直接做相应的除法取余即可,srand()用来设置rand()产生随机数时的随机数种子,在调用rand()函数产生随机数前,必须先利用srand()设好随机数种子(seed), 如果未设随机数种子, rand()在调用时会自动设随机数种子为1。若每次随机数种子都自动设成相同值1,就会导致rand()所产生的随机数值都一样。
产生随机数模块主要要做的工作就是在玩家正式开始之前,由系统程序产生一个两位数字的随机数,即是提供给玩家猜数字的正确结果。产生随机数的代码如下:
srand((unsigned)time(NULL)); //生成两位随机数
do
{
a[0]=(rand()%100)%10;
a[1]=(rand()%100)/10;
}while(!(a[0]!=a[1]));
真正意义上的随机应该是和上一次状态无关的,所以在程序中设置srand()函数就显得尤为重要。srand((unsigned)time(NULL)),这是一个伪随机数种子,用于设定随机数种子,rand()%100用于产生0—99的随机数,然后用a[0]记录三位数字的随机数的十位数字,用a[1]记录两位数字的随机数的各位数字。然后就是判断计算机产生的随机数是否满足条件,要求产生的三位随机数字中没有重复的数字,(a[0]!=a[1])即是判断随机数的十位数字和个位数字不相同。
3.2 游戏规则模块
玩家在进入游戏之前,应该先阅读游戏规则,以便帮助玩家了解猜数游戏的玩法,是玩家能够更熟悉游戏。猜数游戏的游戏规则是“请输入三位整数,与该游戏系统随机产生的两位整数进行比较,若顺序和数字都正确,输出A,只有数字对,则输出B。如果猜测了9次都不正确,则此次游戏失败。”
首先在MFC程序的资源视图(ResourceView)中找到对话项(Dialg),单击其右上角的“+”,然后在VC++6.0软件的菜单中找到插入项(Insert),点击打开,选择Resource type项中选择Dialog,增加一个新的对话框(如图3.1所示),用于显示游戏规则。然后在新的对话框页面单击右键,打开ClassWizard,为该对话框创建一个新类类名为TestDlg1(如图3.2所示)。最后在对话框1中创建一个静态文本框(static),将其名称改为猜数游戏的游戏规则,最后调整对话框中框图的位置。
图3.1 创建新对话框 图3.2 增加对应的类
3.3开始游戏模块
开始游戏模块是猜数游戏的主体部分,主要包括两个部分:对话框显示玩游戏的界面以及用相对应的算法实现猜数游戏。
用对话框显示玩游戏的界面,首先创建一个新的对话框,记为对话框2,并且为其创建一个类TestDlg2,具体做法可以参考3.2节。然后在对话框2中创建6个静态文本框(static),并将其名称从前到后分别改为“输两位数:”,“输出结果:”,“尝试次数:”,“正确答案:”,“游戏耗时:”和“历史记录:”。然后在每个对应的静态文本框后面添加一个显示输出文本框(edit),其中,“结果输出:”后面添加一个显示输出文本框,再添加一个静态文本框,将其名称改为“A”,然后添加一个显示输出文本框,再添加一个静态文本框,将其名称改为“B”。最后,在“输两位数:”这一行的最后增加一个按钮1(button1),将其名称改为“确定”,并且在对话框的右下角增加按钮2(button),将其名称改为“退出”。
在玩家玩猜数游戏的过程中,玩家首次进入游戏界面,在第一个显示输出文本框中输入一个三位数字(数字不重复),然后点击“确定”按钮,“输出结果”和“尝试次数”后面的显示输出文本框就会显示相应的提示信息(如图3.3所示)。同时,如果“输出结果”后面显示的是“3A0B”,表示玩家猜数正确,那么“正确答案”后面就会显示相应的正确答案,并且弹出显示“您赢了”的对话框。如果在“尝试次数”后面显示的数从1一直增加到9,玩家仍然没有猜出正确答案,那么“正确答案”后面就会显示相应的正确答案,并且弹出显示“您输了”的对话框。总之,这些显示输出文本框中显示的内容都是在玩家猜数之后,通过点击“确定”按钮后显示出来的,所以,我们应该为“确定”按钮添加一个消息响应函数onbutton1,这个消息响应函数应该是对话框2的一个成员函数。
其次,猜数游戏的菜单项主要是为了让玩家更方便的完成猜数游戏,同时,也可以建立菜单和对话框之间的连接。首先,在资源视图(rescueview)中找到菜单(meau),菜单显示框,创建两个菜单项,将名称分别改为“启动游戏”和“新游戏” ,同时将其ID改为对话框对应的ID,即为IDD_DIALOG1和IDD_DIALOG2,如图3.4所示。
图3.3 游戏界面
3.4 菜单框图
实现猜数游戏的算法主要就是对玩家猜的数和电脑产生的随机数进行比较,并显示相应的提示信息。首先为每一个显示文本框定义一个变量,numi(0<i<8)即表示第i个显示文本框对应的变量,其中,为“尝试次数”的显示文本框定义了类CTestDlg2的成员变量m_count,并在类CTestDlg的构造函数中初始化为0,还有“历史记录”num7初始化为0。程序代码如下:
int num1,num2,num3,num4,num5,num7,temp;
char ch1[10],ch2[10],ch3[10],ch4[10],ch5[10],ch7[10];
num7=0;
m_count++;
temp=0;
文本框1是提供给玩家输入两位数字的显示,玩家不能输入重复的数字,用num1存放玩家输入的两位数字,具体程序代码如下:
GetDlgItemText(IDC_EDIT1,ch1,10);//获取显示输出文本框1的内容
num1=atoi(ch1);
定义四个整型变量m,n,x和y分别用来存放猜的两位数和是随机数的十位数和个位数,同时用num5存放“正确答案”,即随机数,具体程序代码如下:
int m,n,x,y;
x=num1/10;
y=num1%10;
m=a[0];
n=a[1];
num5=m*10+n;
玩家猜完数字并点击“确定”按钮之后,程序就会转入对玩家猜的数和随机数的各位数字进行比较,如果有个数字的位置和数字都正确,那么num2就加1,如果仅有数字正确而位置不正确,那么num3就加1,具体程序代码如下:
if(m==x&&n==y)
{ num2=2;
num3=0;
temp++;
}
else if((m==x&&n!=y)||(m!=x&&n==y))
{ num2=1;
num3=0;
}
else if(m==y&&n==x)
{ num2=0;
num3=2;
}
else if((m==y&&n!=x)||(m!=y&&n==x))
{ num2=0;
num3=1;
}
else
num2=num3=0;
用itoa()和setdlgitemtext()函数配合输出num2,num3,num4和num7的内容,即是“输出结果”,“尝试次数”以及“历史记录”后面的显示文本框中要显示的内容,具体程序代码如下:
itoa(num2,ch2,10);
itoa(num3,ch3,10);
SetDlgItemText(IDC_EDIT2,ch2);
SetDlgItemText(IDC_EDIT3,ch3);
if(temp>num7)
num7=temp;
num4=m_count;
itoa(num4,ch4,10);//输出文本框4和7的内容
itoa(num7,ch7,10);
SetDlgItemText(IDC_EDIT4,ch4);
SetDlgItemText(IDC_EDIT7,ch7);
如果玩家在尝试次数m_count不超过9次的情况下,猜出了正确答案,那么程序就应该显示num5(对应“正确答案”的选项)的内容,同时弹出“恭喜,您赢了”的对话框。如果玩家在尝试了9次之后,仍然没有猜出正确答案,那么程序也会显示num5(对应“正确答案”的选项)的内容,同时弹出“对不起,您输了”的对话框。具体程序代码如下:
if(num2==2&&num3==0&&m_count<=9)
{
itoa(num5,ch5,10);
SetDlgItemText(IDC_EDIT5,ch5);
MessageBox("恭喜,您赢了");
OnOK();
}
else if(m_count>9)
{
MessageBox("对不起,您输了");
OnOK();
}
4 测试
基于MFC的猜数字游戏是一种智力型游戏。游戏开始执行时,首先是一个菜单框,如图4.1所示,菜单项有三个:启动游戏、新游戏和关于,同时在它们的下方还有三个图标与之一一对应,点击其中任意一个,就会弹出相应的对话框。
点击“帮助”菜单项,就会弹出有关猜数游戏规则的对话框
游戏界面
心得与体会
时间过得很快,历时两周的学习结束,我们组的mfc课题也基本完成。虽然只有短短的两周,但却带给我很多。在小组成员的共同努力下,基本解决了计划中的一个个问题,要实现这个简单的猜数字游戏,也没有想象中那么简单。通过查阅资料 ,虚心请教,终于做完了,游戏虽然简单,但因为自己做的,乐此不疲的玩了好久。最后 也在团队合作中体会到了合作的乐趣与力量。
指导教师评语
参考文献
[1] 钱能.C++程序设计教程[M].北京:清华大学出版,2003
[2] 李春葆.数据结构(C语言版)[M]. 北京:清华大学出版社,2002
[3] 谭浩强.C++程序设计[M].北京:清华大学出版社,2004
[4] 谭浩强.C++程序设计题解与上机指导(第二版)[M].北京:清华大学出版社,2012.
附 录
// StartDlg.cpp : implementation file
//
#include "stdafx.h"
#include "game.h"
#include "StartDlg.h"
#include"time.h"
#include"stdlib.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CStartDlg dialog
int c,a[3]; //定义全局变量
CString str1,str2;
long t1,t2;
CStartDlg::CStartDlg(CWnd* pParent /*=NULL*/)
: CDialog(CStartDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CStartDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
srand((unsigned)time(NULL)); //生成三位随机数
do
{
a[0]=rand()%9+1;
a[1]=rand()%9+1;
a[2]=rand()%9+1;
}while(!(a[0]!=a[1]&&a[0]!=a[2]&&a[1]!=a[2]));
c=0;
t1=GetTickCount();//程序段开始前取得系统运行时间(ms)
}
void CStartDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CStartDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CStartDlg, CDialog)
//{{AFX_MSG_MAP(CStartDlg)
ON_BN_CLICKED(IDC_BUTTON, OnButton)
ON_BN_CLICKED(IDC_ANSWER, OnAnswer)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CStartDlg message handlers
void CStartDlg::OnButton()
{
// TODO: Add your control notification handler code here
int n,b[3];
int l=0,m=0;
n=GetDlgItemInt(IDC_EDIT1);
b[0]=n/100;
b[1]=n%100/10;
b[2]=n%10;
if(n>999||n<100)
MessageBox("请输入三位数");
else if(b[0]==b[1]||b[0]==b[2]||b[1]==b[2])
MessageBox("请输入各不相同的三位数");
else
{
c++;
for(int k=0;k<3;k++)
{
if(a[k]==b[k])
l++;
}
if(a[0]==b[1])
m++;
if(a[0]==b[2])
m++;
if(a[1]==b[0])
m++;
if(a[1]==b[2])
m++;
if(a[2]==b[0])
m++;
if(a[2]==b[1])
m++;
SetDlgItemInt(IDC_EDIT2,l);
SetDlgItemInt(IDC_EDIT3,m);
SetDlgItemInt(IDC_EDIT4,c);
if(l==3)
{
CStartDlg::OnAnswer();
t2=GetTickCount();//程序段结束后取得系统运行时间(ms)
str1.Format("%d.%ds",(t2-t1)/1000,(t2-t1)%1000);//前后之差即程序运行时间
CGameApp *app = (CGameApp *)AfxGetApp();
int static s=0;
long static shorsttime;
s++;
if(s==1||app->e==1)
shorsttime=t2-t1;
else if(shorsttime>(t2-t1))
shorsttime=t2-t1;
str2.Format("%d.%ds",shorsttime/1000,shorsttime%1000);
SetDlgItemText(IDC_EDIT7,str2);
MessageBox("恭喜,您赢了");
OnOK();
}
else if(c==9)
{
CStartDlg::OnAnswer();
MessageBox("对不起,你输了");
OnOK();
}
}
}
void CStartDlg::OnAnswer()
{
// TODO: Add your control notification handler code here
char ch2[10];
int num=a[0]*100+a[1]*10+a[2];
itoa(num,ch2,10);
SetDlgItemText(IDC_EDIT5,ch2);
t2=GetTickCount();//程序段结束后取得系统运行时间(ms)
str1.Format("%d.%ds",(t2-t1)/1000,(t2-t1)%1000);//前后之差即程序运行时间
SetDlgItemText(IDC_EDIT6,str1);
}