第一篇:MFC课程设计报告-一个简单的五子棋游戏(本站推荐)
网络092刘砺锋
设计一个简单的五子棋游戏
一、设计目标与内容
1.了解Windows编程的基础知识,掌握MFC应用程序的基本知识;2.基本掌握面向对象程序设计的基本思路和方法; 3.掌握用VC++开发应用程序的的一般步骤和方法;
4.能够利用所学的基本知识, 设计一个简单的五子棋游戏,具有以下功能:①数据结构的设计;五子棋棋盘的绘制。②两人下棋时,两人下棋算法的设计。③两人下棋时,判断任一方获胜的算法的设计。
二、设计要求
1.用VC++进行编码,实现应用程序的功能。注重编码质量,代码要有适当的注释;
提交设计报告一份(课程设计任务书、目录、主要的数据结构、设计的基本思路、设计的步骤及主要代码、心得体会、参考文献)。总体设计
运行时效果如下:
网络092刘砺锋
图3-1
这个程序只能进行两个人之间的对弈,不能进行人机对弈,由于时间和个人能力的原因所以人机对弈的算法就没有写出。同时程序中也存在着很多漏洞,但基本的功能都已经实现,还有待继续改进。
详细设计
新建工程game_wzq
网络092刘砺锋
选择单文档应用程序,在Step 4 of 6中先中Windows Sockets复选框。如下图:
图3-2
资源编辑
黑白位图Bitmap以表示棋盘上面的棋子: IDB_BLACK DB_WHITE 黑白鼠标Cursor以替换当前鼠标: IDC_CURSOR1 黑棋子
网络092刘砺锋
IDC_CURSOR2 白棋子
黑白图标Icon以显示在状态栏供以提示 IDI_BLACK IDI_WHITE 菜单以供操作: 开始: ID_START 保存: ID_SAVE 打开: ID_OPEN
如下图所示:
图3-3
变量函数
首先,为了实现状态栏的应用,我们必须更改它的变量:
网络092刘砺锋
在MainFrm.h文件里面,把CStatusBar m_wndStatusBar 为public 接着是在game_wzqView.h文件里面添加变量函数: //两个鼠标 HCURSOR hcursorwhite;HCURSOR hcursorblack;//棋盘数组
int wzq[19][19];// colorwhite TRUE时白棋下,否则黑棋下 bool colorwhite;//棋子位图 CBitmap m_bmblack;CBitmap m_bmwhite;//保存文件 void Save();//检查是否结束
void over(CPoint point);//鼠标操作
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);//鼠标图形更换
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
网络092刘砺锋
//菜单的开始
afx_msg void OnStart();//菜单的保存
afx_msg void OnSave();//菜单的打开
afx_msg void OnOpen();
具体实现
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 cs.dwExStyle=cs.dwExStyle|WS_EX_TOPMOST;cs.style=WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;//设置窗口大小:400*340 cs.cx=450;cs.cy=500;
网络092刘砺锋
return TRUE;}
2、初始化变量:
在构造函数里添加初始代码: CGame_wzqView::CGame_wzqView(){ //Load鼠标图像和棋子位图
hcursorblack=AfxGetApp()->LoadCursor(IDC_CURSOR1);
hcursorwhite=AfxGetApp()->LoadCursor(IDC_CURSOR2);m_bmwhite.LoadBitmap(IDB_WHITE);m_bmblack.LoadBitmap(IDB_BLACK);//清理棋盘
//数组值为0表示没有棋子 for(int i=0;i<19;i++)for(int j=0;j<19;j++)wzq[i][j]=0;//白棋先下 colorwhite=true;}
网络092刘砺锋
3、画棋盘:
在OnDraw(CDC* pDC)函数中画棋盘,由于在游戏过程中有可能重画棋盘,而那时棋盘上面有棋子,所以,我们在这个函数里面必须有画棋子的语句。
我们用数组的做为1表示白棋,-1表示黑棋。void CGame_wzqView::OnDraw(CDC* pDC){ CGame_wzqDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);//画背景
CBrush mybrush1;mybrush1.CreateSolidBrush(RGB(192,192,192));CRect myrect1(0,0,1200,800);pDC->FillRect(myrect1,&mybrush1);//画棋盘框线 CPen mypen;CPen*myoldPen;
网络092刘砺锋
mypen.CreatePen(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(Dc.CreateCompatibleDC(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);
网络092刘砺锋
}
else if(wzq[n][m]==-1)
{ //显示黑棋
Dc.SelectObject(m_bmblack);
pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY);
} }
4、设置鼠标:
棋盘画好了,接下来就是下棋了。但鼠标并没有像我们上面说的那样变成白棋,加函数如下:
BOOL CGame_wzqView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message){ if(nHitTest==HTCLIENT){
//白棋下,显示白棋鼠标
if(colorwhite)
{ //调用主框架里面的状态栏
网络092刘砺锋
CMainFrame *pFrm=(CMainFrame*)AfxGetApp()->m_pMainWnd;
CStatusBar*pStatus=&pFrm->m_wndStatusBar;
if(pStatus)
{
pStatus->GetStatusBarCtrl().SetIcon(0,AfxGetApp()->LoadIcon(IDI_WHITE));
pStatus->SetPaneText(0,“白棋下”);
}
SetCursor(hcursorwhite);
}
//显示黑棋鼠标
else
{
SetCursor(hcursorblack);
CMainFrame*pFrm=(CMainFrame*)AfxGetApp()->m_pMainWnd;
CStatusBar*pStatus=&pFrm->m_wndStatusBar;
if(pStatus)
{
网络092刘砺锋
//显示图像
pStatus->GetStatusBarCtrl().SetIcon(0,AfxGetApp()->LoadIcon(IDI_BLACK));
//显示文字
pStatus->SetPaneText(0,“黑棋下”);
}
}
return 1;} return CView::OnSetCursor(pWnd, nHitTest, message);}
5、下棋操作:
这就涉及到OnLButtonDown(UINT nFlags, CPoint point)和OnLButtonUp(UINT nFlags, CPoint point)两个函数了。要用哪一个或用两个?用Down函数时是在鼠标按下时放下棋子,可是,要是我们按下后意识到按错了怎么办;那就改用Up函数,表示当鼠标键松开时放下棋子。OK!添加函数如下:
void CGame_wzqView::OnLButtonUp(UINT nFlags, CPoint
网络092刘砺锋
point){ CView::OnLButtonUp(nFlags, point);CDC *pDC=GetDC();CDC Dc;if(Dc.CreateCompatibleDC(pDC)==FALSE)
AfxMessageBox(“Can't create DC”);//是否在棋盘内
if(point.x>30&&point.x<410&&point.y>30&&point.y<410){
int px=(point.x-30)/20;
int py=(point.y-30)/20;
//是否已经有棋子
if(colorwhite&&wzq[px][py]==0)
{
Dc.SelectObject(m_bmwhite);
pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);
//表示存在白棋
wzq[px][py]=1;
//检查是否结束
网络092刘砺锋
over(point);
//换黑棋下
colorwhite=false;
}
else if(wzq[px][py]==0)
{
Dc.SelectObject(m_bmblack);
pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);
wzq[px][py]=-1;
over(point);
colorwhite=true;
} } }
由上面可以看出,当鼠标键松开时判断,如果那个位置没有棋子,则放下,并把棋盘数组赋相应的值:1或-
16、是否结束:
网络092刘砺锋
接着是用一个over()函数判断是否结束,是则结束并重新开始;否则,接着把鼠标变成对方棋子,表示对方下棋。那over()函数又是怎样的呢?
此函数是利用刚下棋的位置为中心,检查它各个方向上的连续五个棋子是否同色,是则结束并重新开始。
然而,我们又是怎样判断一个方向上的五个棋子的同色的?这就涉及地为什么我要把五子棋数组赋值为1和-1的问题。因为这样有一个好处:利用连续五个棋子的值相加,如果它们的值的绝对值等于5,则说明是同色。当然,这只是这样赋值的一点作用,真正的作用将在后面介绍。添加如下: void CGame_wzqView::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)
网络092刘砺锋
yy=0;else yy=y-4;int i,j,a;//横向判断
for(i=xx;i<15;i++){
a=0;for(j=i;j
{
a=a+wzq[j][y];//五个都是白棋 if(a==5){
AfxMessageBox(“白棋胜!”);//重新开始 OnStart();
return;
} //五个都是黑棋 if(a==-5){
网络092刘砺锋
AfxMessageBox(“黑棋胜!”);OnStart();
return;}
} } //竖向判断
for(i=yy;i<15;i++){
a=0;for(j=i;j
return;} if(a==-5)
网络092刘砺锋
{ AfxMessageBox(“黑棋胜!”);OnStart();
return;} } } //向右下角 //判断起点位置 if(x yy=y-x;} else { if(yy==0) xx=x-y;} //参数over=1时退出循环 int over=0;do 网络092刘砺锋 { 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 网络092刘砺锋 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 { 网络092刘砺锋 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) { 网络092刘砺锋 AfxMessageBox(“白棋胜!”); OnStart(); return; } if(a==-5) { AfxMessageBox(“黑棋胜!”); OnStart(); return; } } //到了边界 else over=1; } xx-=1; yy+=1;} while(over==0);} 网络092刘砺锋 文件保存和读取 1、保存文件函数是一个菜单选项。它的作用就是保存当前游戏的状态。首先,我们应该为我们自己的文件定义一个后缀名:.wzq;接着是打开保存文件的公共对话框,如果确定,则表示保存,那么就先获取文件名,然后按照一定的顺序保存各个点的数组的值,最后保存当前是哪种颜色下棋。void CGame_wzqView::OnSave(){ //设置保存的文件,后缀名wzq CFileDialog dlg(FALSE,“wzq”,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,“(*.WZQ)|*.wzq|All Files|*.*||”,this);//如果公共类对话框为确定 if(dlg.DoModal()==IDOK)//获取文件名 dlg.GetFileName();//否则,退出 else return;//字符串变量 CString str;int i,j; 网络092刘砺锋 CStdioFile file;//如果有问题,退出 if(file.Open(dlg.GetFileName(),CFile::modeCreate|CFile::modeWrite|CFile::typeText)==0){ AfxMessageBox(“save error!”); return;} //循环把棋盘数组的值写进文件 for(i=0;i<19;i++) for(j=0;j<19;j++) { if(wzq[i][j]==-1) file.WriteString(“-1n”); if(wzq[i][j]==0) file.WriteString(“0n”); if(wzq[i][j]==1) file.WriteString(“1n”); } //保存当前下棋颜色 if(colorwhite==true) file.WriteString(“1n”); 网络092刘砺锋 else file.WriteString(“0n”); //关闭文件 file.Close();} 2、读文件就是把我们以前保存的文件打开,读取当前打开文件的内容,并给数组赋值使和文件内容相同,然后可以继续进行游戏。 void CGame_wzqView::OnOpen(){ CFileDialog dlg(TRUE,“wzq”,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,“(*.WZQ)|*.wzq|All Files|*.*||”,this);if(dlg.DoModal()==IDOK) dlg.GetFileName();else return;CString str;int i,j,m;CStdioFile file;if(file.Open(dlg.GetFileName(),CFile::modeRead)==0) 网络092刘砺锋 { AfxMessageBox(“open error!”); return;} CArchive ar(&file,CArchive::load);for(i=0;i<19;i++) for(j=0;j<19;j++) { ar.ReadString(str); sscanf(str,“%d”,&m); if(m==-1) wzq[i][j]=-1; if(m==0) wzq[i][j]=0; if(m==1) wzq[i][j]=1; } ar.ReadString(str); sscanf(str,“%d”,&m); if(m==1) colorwhite=true; else 网络092刘砺锋 colorwhite=false; file.Close(); ar.Close(); Invalidate(false);} OK,现在我们的程序已经完成了,可以两个人进行对弈了。 软件工程设计 专 业:班 级:姓 名:学 号:指导老师: I 目录 第一章 需求分析.......................................................1 1.1 总体分析..........................................................1 1.2 初始化............................................................1 1.3 主循环控制模块....................................................1 1.4 玩家下子..........................................................1 1.5 盘面分析填写棋型表................................................2 1.6 对方下子..........................................................2 1.7 胜负判断..........................................................2 第二章 功能描述.......................................................3 2.1 功能模块图........................................................3 2.2 功能说明..........................................................3 第三章 系统设计.......................................................4 3.1 流程图............................................................4 3.2 流程图说明........................................................5 第四章 运行结果.......................................................6 第五章 总结...........................................................7 附录一 源代码.........................................................8 II 软件工程设计 五子棋游戏 第一章 需求分析 1.1 总体分析 软件需求分析是软件开发周期的第一个阶段,也是关系到软件开发成败的关键一步。对于任何一个软件而言,需求分析工作都是至关重要的一步。只有通过软件需求分析,才能把软件的功能和性能由总体的概念性描述转化为具体的规格说明,进而建立软件开发的基础。实践表明,需求分析工作进行得好坏,在很大程度上决定了软件开发的成败。 软件需求分析的任务是:让用户和开发者共同明确将要开发的是一个什么样的软件。具体而言,就是通过对问题及其环境的理解、分析和综合,建立逻辑模型,完成新软件的逻辑方案设计。 基于本游戏,首先得为整个棋盘建立一张表格用以记录棋子信息,我们使用一个15*15的二维数组Table[15][15](15*15是五子棋棋盘的大小),数组的每一个元素对应棋盘上的一个交叉点,用‘0’表示空位、‘1’代表己方的子、‘2’代表对方的子;这张表也是今后分析的基础。在此之后还要为两个玩家双方各建立一张棋型表Computer[15][15][4]和Player[15][15][4],用来存放棋型数据。 1.2 初始化 首先,建立盘面数组Table[15][15]、对战双方的棋型表Computer[15][15][4]和Player[15][15][4]并将它们清零以备使用;然后初始化显示器、键盘、鼠等输入输出设备并在屏幕上画出棋盘(棋盘可以不显示)。 1.3 主循环控制模块 控制下棋顺序,当轮到某方下子时,负责将程序转到相应的模块中去,主要担当一个调度者的角色。 1.4 玩家下子 当轮到玩家下时,您通过键盘或鼠标在棋盘上落子,程序会根据该点的位置,在Table[15][15]数组的相应地方记录‘2’,以表明该子是玩家下的。 软件工程设计 1.5 盘面分析填写棋型表 您在下五子棋时,一定会先根据棋盘上的情况,找出当前最重要的一些点位,如“活三”、“冲四”等;然后再在其中选择落子点。先来分析己方的棋型,我们从棋盘左上角出发,向右逐行搜索,当遇到一个空白点时,以它为中心向左挨个查找,如果遇到己方的子则记录然后继续,如果遇到对方的子、空白点或边界就停止查找。左边完成后再向右进行同样的操作;最后把左右两边的记录合并起来,得到的数据就是该点横向上的棋型,然后把棋型的编号填入到Computer[x][y][n]中就行了(x、y代表坐标,n=0、1、2、3分别代表横、竖、左斜、右斜四个方向)。而其他三个方向的棋型也可用同样的方法得到,当搜索完整张棋盘后,己方棋型表也就填写完毕了。然后再用同样的方法填写对方棋型表。 注意:所有棋型的编号都要事先 定义好,越重要的号数越大! 1.6 对方下子 有了上面填写的两张棋型表,就是遍历棋型表Computer[15][15][4]和Player[15][15][4]找出其中数值最大的一点,在该点下子即可。但这种算法的弱点非常明显,只顾眼前利益,不能顾全大局,这就和许多五子棋初学者一样犯了“目光短浅”的毛病。如果在这儿下子将会形成对手不得不防守的棋型(例如:‘冲四’、‘活三’);那么下一步对手就会照您的思路下子来防守您,如此一来便完成了第一步的预测。这时再调用模块4对预测后的棋进行盘面分析,如果出现了‘四三’、‘双三’或‘双四’等制胜点,那么己方就可以获胜了(当然对黑棋而言‘双三’、‘双四’是禁手,另当别论);否则照同样的方法向下分析,就可预测出第二步、第三步„„ 等一等,要是盘面上没有对手必须防的棋型,哪该怎么办呢?进攻不成的话就得考虑防守了,将自己和对手调换一下位置,然后用上面的方法来预测对手的棋,这样既可以防住对手巧妙的攻击,又能待机发动反击,何乐而不为呢! 1.7 胜负判断 务须多言,某方形成五子连即获胜;若黑棋走出‘双三’、‘双四’或长连即以禁手判负。 软件工程设计 第二章 功能描述 2.1 功能模块图 五子棋游戏判断棋盘是否已满判断是否出错并提示判断那方获胜交替循环双方下棋 图2.1 功能模块图 2.2 功能说明 该五子棋程序基本上实现了五子棋的游戏功能,有双方下棋的界面及最终判定结果的界面。同时该游戏采用二维坐标实现,明了易懂,方便玩家在游戏过程中的基本操作,使游戏更加简便。在细节方面,该系统提供实时存储功能,随时记录为完成的游戏,使用户可以很好的处理意外中断的情况。该游戏基本实现了游戏的一些要求和特征。在游戏的源程序及文档方面,我们也严格遵守软件工程思想,立足实验要求,确定任务,需求分析,设计和编码,每个步骤力求清晰易懂。原代码注释详尽,各功能模块功能分明,可移植性强。当然该系统也有很多不足的地方,第一次进行独立的课程设计,也有很多细节方面是考虑到的,这款游戏也是在不断的调试和修改中产生和完善的。希望老师能够指出不足,帮助我不断提高。 软件工程设计 第三章 系统设计 3.1 流程图 开始棋盘已满是输出平局否“0”方选位置判断该位置是否有棋有另找位置无“0”方落子否判断“0”方是否获胜是输出“0”方获胜否棋盘已满是输出平局结束否“x”方选位置判断该位置是否有棋有另找位置无“x”方落子判断“x”方是否获胜是输出“x”方获胜 图3.1 流程图 软件工程设计 3.2 流程图说明 本程序定义了各种操作函数、各种状态判定宏,思想明确,思路清晰。各个判断选择了不同路径,因此继续进行或输出结果。程序中,“循环”的利用非常直接和清晰,双方交替下棋,因此循环往复。最终决出胜负或最终平局。分析时,也考虑了许多种情况,针对各个情况均作出了相对措施和解决方案。 程序采用循环进行双方交替下棋,并进行了很多判断。首先判断棋盘是否已满,若棋盘已满,则输出平局,结束游戏;若棋盘未满,则继续进行。然后判断“0”方是否胜出,若“0”方获胜,则输出“0”方获胜,结束游戏;若“0”方没有获胜,则继续进行。再判断“x”方是否获胜,若“x”方获胜,则输出“x”方获胜,结束游戏;若“x”方没有获胜,则继续进行。回到“首先”的判断。如此循环„„ 软件工程设计 第四章 运行结果 图4.1 运行结果初始图 图4.2 游戏过程图 图4.3 软件工程设计 图4.4 图4.5 软件工程设计 图4.6 图4.7 软件工程设计 图4.8 游戏进行图 图4.9 “0”方获胜图 软件工程设计 附录一 源代码 #include using namespace std; int Hsheng(char a[][15]); //判断o子是否获胜的函数 int Bsheng(char a[][15]); //判断x子是否获胜的函数 int he(char a[][15]); //判断是否平局(也就是棋盘下满了)的函数 void qipan(char a[15][15]) //执行输出棋盘命令 { cout<<“本游戏采用二维数组实现,棋盘为15X15的二维直角坐标系,均从1到15,祝二位游戏愉快.”;for(int i=0;i<15;i++) //打印棋盘 { for(int j=0;j<15;j++)cout< } } int main(){ char a[15][15]; int x,y; for(int i=0;i<15;i++) for(int j=0;j<15;j++) a[i][j]=' ';qipan(a); while(1)//用循环语句执行o,x交替下子,这些while语句看起来似乎是个死循环~实际上都会经过break结束 { int a1=1; while(1) { for(;a1;) { cout<<“请输入o子下的位置:”; //输入o子的位置 cin>>x>>y;if(a[x][y]=='o'||a[x][y]=='x') //判断是否已有子 {cout<<“已有子请重下”<<“,”;continue;} else if(x>=15||y>=15){cout<<“输入错误请重输”<<“,”;continue;} else { a[x][y]='o';a1=0;} } break;} 软件工程设计 qipan(a); //下好o子后将棋盘显示 if(Hsheng(a)) //判断o子是否已经获胜 {cout<<“o子获胜”< while(1) //下x子 { cout<<“请输入x子下的位置:”; cin>>x>>y; if(a[x][y]=='o'||a[x][y]=='x'||x>=15||y>=15) { for(;a[x][y]=='o'||a[x][y]=='x';) { cout<<“已有子请重下”; cout<<“请输入x子下的位置:”; cin>>x>>y;continue;} for(;x>=15||y>=15||x;) { cout<<“输入错误请重输”<<“,”; //判断输入棋子位置是否正确 cout<<“请输入x子下的位置:”; cin>>x>>y;continue;} a[x][y]='x';break; } else {a[x][y]='x';break;} } qipan(a); //再一次输出棋盘 if(Bsheng(a)) //判断x子是否已经获胜 {cout<<“x子获胜”< if(he(a)) //判断是否平局 {cout<<“平局”< } return 0; } int Hsheng(char a[][15]){ int i,j; //判断横着的5个是否都相等 for(i=0;i<15;i++) for(j=0;j<15;j++) if(a[i][j]=='o'&&a[i][j+1]=='o'&&a[i][j+2]=='o'&&a[i][j+3]=='o'&&a[i][j+4]=='o') return 1; for(j=0;j<15;j++) //判断竖着的5个是否都相等 for(i=0;i<15;i++) if(a[i][j]=='o'&&a[i+1][j]=='o'&&a[i+2][j]=='o'&&a[i+3][j]=='o'&&a[i+4][j]=='o') 软件工程设计 return 1; for(i=0;i<15;i++) //判断左斜5个 for(j=0;j<15;j++) if(a[i][j]=='o'&&a[i+1][j+1]=='o'&&a[i+2][j+2]=='o'&&a[i+3][j+3]=='o'&&a[i+4][j+4]=='o') return 1; for(i=0;i<15;i++) //右斜5个 for(j=14;j>3;j--) if(a[i][j]=='H'&&a[i+1][j-1]=='o'&&a[i+2][j-2]=='o'&&a[i+3][j-3]=='o'&&a[i+4][j-4]=='o') return 1; return 0; } int Bsheng(char a[][15]) //同o,只是改字符 { int i,j; for(i=0;i<15;i++) for(j=0;j<15;j++) if(a[i][j]=='x'&&a[i][j+1]=='x'&&a[i][j+2]=='x'&&a[i][j+3]=='x'&&a[i][j+4]=='x') return 1; for(j=0;j<15;j++) for(i=0;i<15;i++) if(a[i][j]=='x'&&a[i+1][j]=='x'&&a[i+2][j]=='x'&&a[i+3][j]=='x'&&a[i+4][j]=='x') return 1; for(i=0;i<15;i++) for(j=0;j<15;j++) if(a[i][j]=='x'&&a[i+1][j+1]=='x'&&a[i+2][j+2]=='x'&&a[i+3][j+3]=='x'&&a[i+4][j+4]=='x') return 1; for(i=0;i<15;i++) for(j=14;j>3;j--) if(a[i][j]=='x'&&a[i+1][j-1]=='x'&&a[i+2][j-2]=='x'&&a[i+3][j-3]=='x'&&a[i+4][j-4]=='x') return 1; return 0; } int he(char a[][15]) { for(int i=0;i<15;i++) for(int j=0;j<15;j++) { if(a[i][j]==' ') //当棋盘全部子都不是' '时才能return 1,即棋盘已下满 return 0; } return 1; } -《软件技术基础》项目报告 《软件技术基础》课程项目报告 项目名称:用VC++实现的五子棋游戏 专业班级: G11009 项目成员:崔光浩 程德武 付强 付钰 李洪润 尚振兴 沈婷玉 佟承雨 周彤姣 指导老师: 殷黎 完成时间: 2011/10/31 -《软件技术基础》项目报告 目录 摘要 一、需求分析.............................................................................................................1.1 开发背景................................................................................................................1.2 项目目标................................................................................................................1.3 运行环境................................................................................................................1.4 游戏说明................................................................................................................1.5 项目任务书............................................................................................................二、技术路线.......................................................................................................................2.1 总体方案................................................................................................................2.2 详细设计................................................................................................................三、工程进度.......................................................................................................................3.1 前期准备部分(1-3)天.......................................................................................3.2中期实现功能部分(4-7)天................................................................................3.3后期总结完善部分(8-10)天..............................................................................四、测试报告.....................................................................................................................4.1 第5天:测试棋谱..............................................................................................4.2 第6天:基本功能测试......................................................................................4.3 第7天:悔棋功能..............................................................................................4.4第8天:测试其他的附加功能...........................................................................4.5第9、10天:总体测试.......................................................................................五、个人小结.....................................................................................................................六、主要算法.....................................................................................................................1判断胜负..................................................................................................................-《软件技术基础》项目报告 2鼠标模拟..................................................................................................................摘要 五子棋是起源于中国古代的传统黑白棋种之一。现代五子棋日文称之为“連珠”,英译为“Renju”,英文称之为“Gobang”或“FIR”(Five in a Row的缩写),亦有“连五子”、“五子连”、“串珠”、“五目”、“五目碰”、“五格”等多种称谓。 五子棋不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。五子棋既有现代休闲的明显特征“短、平、快”,又有古典哲学的高深学问“阴阳易理”;它既有简单易学的特性,为人民群众所喜闻乐见,又有深奥的技巧和高水平的国际性比赛;它的棋文化源渊流长,具有东方的神秘和西方的直观;既有“场”的概念,亦有“点”的连接。它是中西文化的交流点,是古今哲理的结晶。 -《软件技术基础》项目报告 一、需求分析 1.1 开发背景 五子棋是一种两人对弈的纯策略型棋类游戏,是起源于中国古代的传统黑白棋种之一。发展于日本,流行于欧美。五子棋容易上手,老少皆宜,而且趣味横生,引人入胜;不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。传统五子棋的棋子分为黑白两色,采用围棋棋盘,棋子放置于棋盘线交叉点上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个同色棋子连成不间断的一排者为胜。 棋类游戏规则简单,对外部要求不高,人们可以随时随地进行对弈。但是,真正能够精通棋类游戏的人却不是很多,主要是棋类游戏具有变化莫测的特点,人们经常得在棋局上深思熟虑才能找到克敌制胜的办法。因此,各种棋类游戏都具有开发智力的效能。在休闲中使自己得到真正的长进,这或许就是其倍受人们青睐的原因所在! 1.2 项目目标 我们将主要通过VC++语言,运用面向对象的程序设计方法,开发此款五子棋游戏。力争使程序短小精悍,简洁明了;游戏界面优美,容易操作;功能丰富,趣味性强。现在将我们对项目期望实现的目标一一介绍: a.能供两人对弈。 b.可以实现经典棋局的回放。c.可以悔棋。 D.能实现棋局步数的排名。e.操作方便,容易上手。 1.3 运行环境 本游戏短小精悍,而且对电脑配置的要求均不高,目前几乎所有的PC机均可运行该游戏。但是为了能让大家更好地体验该款游戏,我们给出如下的最低配置: -《软件技术基础》项目报告 最低配置:CPU1GHz 内存 32M 硬盘 4G Windows 95 Microsoft Visual C++ 6.0 同时结合我们开发该游戏的环境,我们强烈推荐用户使用如下的配置: 最佳配置:CPU2GHz及以上 内存256M及以上 硬盘80G及以上 Windows XP及以上 Microsoft Visual C++ 6.0 1.4 游戏说明 (1)游戏流程: 启动游戏后,显示主菜单。里面包括开始游戏、读取游戏、排行榜、关于游戏、游戏帮助、退出。读取游戏中包含经典棋局的回放和未下完的棋局。排行榜中显示的是棋局步数的排名。(2)游戏规则: 落子:对阵双方交替落子,任何一方不能多下一步,也不能在已有棋子的地方继续落子,否则会有错误提示。 赢棋:任何一方先出现在棋盘的横、纵或斜线上形成连续的五子,则该方获胜。排行榜:可以判断赢者是否可以进入排行榜。 (3)特别提示: 双人对弈模式下,悔棋的过程为:首先由玩家向对方发送悔棋请求(悔棋消息),然后由对方决定是否允许玩家悔棋,在玩家得到对方的响应消息(允许或者拒绝)之后,才进行悔棋与否的操作。 1.5 项目任务书 (1)班级:G11009(2)组长:崔光浩 (3)成员:尚振兴、李洪润、沈婷玉、程德武、付强、周彤娇、佟承雨、付钰(4)项目名称: 五子棋 -《软件技术基础》项目报告 (5)项目背景:五子棋是起源于中国古代的传统黑白棋种之一。五子棋不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。五子棋既有现代休闲的明显特征“短、平、快”,又有古典哲学的高深学问“阴阳易理”。 (6)项目分工:程德武:显示棋盘、显示帮助、游戏版权、显示在线帮助文档。 李洪润:排行榜全部。 付 强:下棋、经典棋局的回放。 沈婷玉:构造函数、初始化所有数据、析构函数。 尚振兴:负责文档规划。 周彤姣:文件的保存和读取。 付 钰:技术支持。 (7)项目进度:1-3天:前期准备,各类资料收集,流程图,算法等同时完成。4-7天:实现基本功能和附加功能。8-10天:最后测试,最后优化。 (各类文档的设计与编写穿插于始终) -《软件技术基础》项目报告 二、技术路线 2.1 总体方案 游戏的简单流程如下图: 开始开始游戏 读取游戏排行榜帮助否关于退出 设定用户信息 下棋判断是否赢是悔棋鼠标模拟进排行榜否 是否保存保存是认输 2.2 详细设计 a.实现玩家的类Player。其中实现各个成员变量的set和get方法,以及构造和析构函数。 b.实现五子棋的类Gobang。其中实现各个功能的函数如下: 1.开始游戏 :开始、下棋、悔棋、认输、保存 void startGame();2.显示帮助文档 void showHelp();3.显示版权 void showEdition();4.显示棋盘 void showChessBoard();5.显示在线在帮助文档 void showOnlineHelp(); -《软件技术基础》项目报告 6.显示菜单 void showMenu();7.播放历史棋局 void movieGoBang();8.读取排行榜 void setWinnerHome();9.显示排行榜 void showWinnerHome();10.修改排行榜 void editWinnerHome(Player &player);11.保存历史记录、保存经典、继续 int saveHD(char *arr);12.读取历史记录、读取经典、继续 int loadHD(char *arr);13.鼠标模拟 1.void monitor();2.bool agree(int a);14.悔棋 void repent();15.判断五子连线 bool fun();16.显示已保存文档、有经典文档和继续文档 int showSave(int choice); -《软件技术基础》项目报告 三、工程进度 本次作业历时十天时间,总的说起来可以分为三个部分: 3.1 前期准备部分(第1-3天) 主要完成方案的讨论、资料的收集、流程图、算法等前期工作,编写报告中需求分析的一部分和总体方案的一部分及项目任务书。 第1天:选定题名,进行方案的讨论。收集资料,写出需求分析中的开发背景部分。第2-3天:商定算法,将软件总体分块,写出项目任务书。 3.2中期实现功能部分(第4-7天) 主要完成预先设想的基本功能和附加功能,完成项目文档中的技术路线和需求分析的剩余部分,完成调试报告中的单功能调适。 第4天:开始软件编写,完成棋盘的设计部分。完成下棋部分程序的编写,测试棋盘设计部分。完成悔棋部分程序的编写,测试下棋部分程序。 第5-6天:完成两人对弈部分程序的编写,测试基本功能,测试悔棋部分。 第7天:完成附加功能,测试对弈部分情况。完善各项功能,测试附加功能。 3.3后期总结完善部分(第8-10天) 总体功能的调试改进,完成项目文档。 第8天:功能调试,优化软件。第9-10天:其它报告剩余部分。 0-《软件技术基础》项目报告 四、测试报告 4.1 第4天:测试棋谱 测试人:程德武 测试内容:通过VC++语言,我们编写了基于对话框的游戏界面。我们将测试它的最基本面貌,希望其是符合我们要求的界面模式! 测试过程:运行程序以后,弹出了棋局的对话框,整体棋局呈现白色,棋局右边和上边都有一些空出的区域,这是我们为将来实现其他附加功能预留的地方。但是测试过程中我们始终发现一个问题,棋格的位图没有按照我们的设想正确放置,出现了跃出棋盘的怪现象! 问题解决:通过反复推敲程序相关部分,我们觉得可能是算法有疏漏,于是修改了一下算法。我们将每个棋格的位图封装了起来,将其位置作为成员数据。再运行程序,问题就不再出现了。反复运行程序再也没有出现异常情况! 4.2 第5天:基本功能测试 测试人:佟承雨 测试内容:程序的基本框架已经编好了,希望能按我们的预期目标能实现供两人对弈功能! 测试过程:将程序编译、链接、运行后,弹出棋局的对话框,然后开始下棋,我们一步一步观察了每步落子后棋局的变化过程,均与我们预期的变化过程一样,我们非常欣喜,这说明我们设计下棋程序时的算法准确无误! 1-《软件技术基础》项目报告 4.3 第6天:悔棋功能 测试人:沈婷玉 测试内容: 测试悔棋功能能否正常实现。 测试过程:我们测试了悔棋功能,反复的测试,此功能运行良好,能够一步一步悔棋,直到无子可悔,而且通过记录观察,每步的悔棋过程均准确无误! 4.4第7天:测试其他的附加功能 测试人:李洪润 测试内容:我们对排行榜,经典棋局回放功能进行了测试 测试过程:我们开始游戏后,正常下棋取得胜利后,程序默认将胜方步数与排行榜中已存在的玩儿家步数进行比较,满足条件则进入排行榜,否则不进入。如果是前五次玩的话默认都进入排行榜。关于经典棋局,进入游戏后,载入经典棋局,每按一次回车,回放一步,经测试无异常。 4.5第8-10天:总体测试 测试人:崔广浩 测试内容:我们将在总体上对程序的运行效果和运行环境进行测试,以完善所有功能,达到软件的最优化! 测试过程: 首先我们测试了软件对运行环境的要求,我们先在Windows xp操作系统中运行了一遍,结果并无异常情况出现;随后我们在CPU只有1GHz频率的硬件系统中运行,也并未发现任何问题!我们基本上可以得出结论,我们的软件在一般的软、硬件环境下都能正常稳定地运行,对机子的配置基本没有什么要求,适合于广大的电脑拥有者使用! 2-《软件技术基础》项目报告 五、个人小结 通过本次五子棋程序的设计,我想我学到了很多东西。我主要是负责大报告的设计和编写,同时也编写一部分程序。编程的确是非常辛苦,主要是第一次利用VC++编写程序。我的VC++学的不是很好,编程时遇到了很多的困难,好在我们班的崔光浩同学不辞辛苦的帮助我调试程序,我们参考了很多的资料,像我们《软件基础》大作业中的优秀作品,网上的有关VC++的一些实用的编程技巧,还有一些经典的算法。善于利用已有的资源,是我在本次大作业中学到得很宝贵的经验,对于网络,我们懂得了不只要用它来实现低级的聊天、看电影、打游戏等功能,我们还要实现获取有用的信息、学习别人先进的方法、查阅相关的电子资料等功能,充分发挥它的作用。 我们这次的作品,虽然只是一个比较简单游戏的实现,但是通过这次合作,我也有很大的收获。虽然是一个小组,但每个人有不同的看问题的方法,我们在做这个软件的时候,大家也曾有过意见的分歧,我们也曾因为预期的功能没有实现而互相指责,也曾为有一点小小的进步而欣喜若狂。一路风风雨雨,我们毕竟是基本完成了预期的功能,努力没有白费,心里特别的充实,这就是创造的快乐,也是合作的快乐。我还编写了大作业的文档,由于自身能力的有限,我虽然竭尽全力,但总的感觉还是不能让我满意,不过,我也从中学到了一些东西: 总的说来,这次作业中我锻炼了自己的能力,也培养了团队合作的精神,感谢我的队友!感谢老师!感谢生活! ——尚振兴 在本软件的开发中,还是有不少地方是不太满意的,首先我通过单纯的VC++语言来实现功能。虽然VC++的强大功能足以应付这种软件的开发要求了,但是由于控件的缺乏,语法的繁琐,在实现很多功能的实现中花费了不少力气。但是由于知识水平的缺乏,无法使用Delphi,flash等更简便的手段来实现功能,希望以后可以弥补这一点。其次,缺乏完成大工程的经验,没有建立一个有效可行的工作计划。在工作中时而通宵奋战,时而停顿不前,既浪费了宝贵的时间,也耗费了大量精力,实在得不偿失。 但值得注意的是,在软件的开发中,我也学到了很多书本上学不到的经验。首先,我使用了别人开发的思想,虽然开始很不习惯,但是上手之后就深深的体会到模块化开发和面向对象语言的方便与高效。其次,这次开发中我使用了很多以前没接触过的函数。除了上网学习资料外。熟练的使用老师提供的参考资料,相信对我以后的工作会有很大的益处。 3-《软件技术基础》项目报告 我觉得通过这个小项目很好地考察了每个学生的知识水平,每个人都发挥了个人最佳的状态。虽然这种形式比普通的卷面考试要难,但是却能够充分调动每个人的积极性去学习自己尚未接触的知识和运用自己已经学过的知识。实验中,我们小组成员之间配合默契,共同商讨问题,使我们在碰到一个一个的问题后,能快速找到相关的答案。难能可贵的是,在软件制作最紧张的时候,大家都义无反顾地放弃了一点休息时间,和团队一起坚持奋斗!所以通过这次实验的合作,我们小组成员之间更加增进了了解,增进了团结! ——崔光浩 六、主要算法 以下时详细算法中提到的主要函数,其他函数在程序中。 1判断胜负 五子棋的胜负,在于判断棋盘上是否有一个点,从这个点开始的右、下、右下、左下四个方向是否有连续的五个同色棋子出现,判断胜负方向如下图 bool Gobang::fun(int a, int b)//判断是否五子连棋 { int arr[8][2]={{-1,-1},{1,1},{-1,0},{1,0},{-1,1},{1,-1},{0,1},{0,-1}};int i;int sum=0;for(i=0;i<8;i+=2) 4-《软件技术基础》项目报告 { sum=1;int A = a+arr[i][0];int B = b+arr[i][1];while(A>=0 && A<=N && B>=0 && B<=N && chessBoard[A][B]==chessBoard[a][b]) { } A = a+arr[i+1][0];B = b+arr[i+1][1];while(A>=0 && A<=N && B>=0 && B<=N && A += arr[i][0];B += arr[i][1];sum++;chessBoard[A][B]==chessBoard[a][b]) } } return false;{ } if(sum>=5)return true;A += arr[i+1][0];B += arr[i+1][1];sum++; 5-《软件技术基础》项目报告 2鼠标模拟 void Gobang::monitor(){ char ch = getch();switch(ch){ case 'w': case 'W': getMonitor = 0;break;case 'a': case 'A': getMonitor = 1;break;case 's': case 'S': getMonitor = 2;break;case 'd': case 'D': getMonitor = 3;break;case 'j': case 'J': getMonitor = 4; 6-《软件技术基础》项目报告 } } break;case 'r': case 'R': getMonitor = 5;break;case 'p': case 'P': getMonitor = 6;break;case 'Q': case 'q': getMonitor = 7;break;default: break;void Gobang::startGame(){ int chose = 1;while(chose){ showChessBoard();monitor(); 7-《软件技术基础》项目报告 if(getMonitor==0 && chessBoardMark[0]-1>=0)chessBoardMark[0]--;if(getMonitor==1 && chessBoardMark[1]-1>=0)chessBoardMark[1]--;if(getMonitor==2 && chessBoardMark[0]+1 case 4: //下棋 if(playGoBang(chessBoardMark[0], chessBoardMark[1])==1) if(fun()==true){ system(“cls”); printf(“nnnnn You Win!!nnnnnnnnnn”); remind(); if(historyStep%2==0)else editWinnerHome(player2);editWinnerHome(player1); showWinnerHome();remind(); 8-《软件技术基础》项目报告 } } } } break;case 5: //悔棋 repent();break;case 6: //保存 showSave(1);break;case 7: //退出 chose = 0;break;default: break; 《Java语言程序设计》 课程设计报告 学 院: 信息科学技术学院 班 级: 软件技术2班 姓 名: 王更新 学 号: 1108900505 指导教师: 郭韶升 课设时间: 2014-03-17 至2014-03-26 二O一四 年 三月 二十六 日 目 录 一、设计要求…………………………………………....2 二、设计步骤…………………………………………....2 2.1程序流程图………………………….…………...2 2.2程序的功能分配……………………….………...3 三、设计正文…………………………………………....6 3.1创建棋盘类……………………………………….6 3.2判断输赢功能实现……………………………….9 3.3测试结果……………………………………….....9 四、心得体会…………………………………………...12 五、参考文献…………………………………………...12 附录(源代码)……………………………………….13 一、课程设计要求 设计一个15╳15围棋棋盘,由两玩家交替进行对战,并可以实现以下功能: 1.选择落子的先后顺序 2.重置棋盘 3.刷新重新开始 4.退出提示 并且规定退出者判为负,但退出过程中要有提示。以防不小心点错了。最后判断某一方是否为五子连珠。 实现一个简单的多用户五子棋的游戏程序,包括如下两个界面(1)选择对弈桌(执黑、执白)。 (2)在游戏界面,有开始,退出(游戏未结束、点退出自动判负); 二、设计步骤 2.1程序流程图 2.2 程序的功能分配 a.棋盘的绘制 public void draw_qipan(Graphics G)//画棋盘 15*15{ G.setColor(Color.lightGray); G.fill3DRect(10,10,300,300,true); G.setColor(Color.black); for(int i=1;i<16;i++){ G.drawLine(20,20*i,300,20*i); G.drawLine(20*i,20,20*i,300); } } b.添加按钮 Button b1=new Button(“开始”);Button b2=new Button(“重置游戏”);Label lblWin=new Label(“ ”);Checkbox ckbHB[]=new Checkbox[3];Button exist = new Button(“退出”);public void init(){ ckbHB[0]=new Checkbox(“执白”,ckgHB,false); ckbHB[1]=new Checkbox(“执黑”,ckgHB,false); ckbHB[2]=new Checkbox(“观看”,ckgHB, false);} c.鼠标棋子的触发事件 public void mouseClicked(MouseEvent e){ Graphics g=getGraphics(); int x1,y1; x1=e.getX(); y1=e.getY(); if(e.getX()<20 || e.getX()>300 || e.getY()<20 || e.getY()>300){ return; } if(x1%20>10){ x1+=10; } if(y1%20>10){ y1+=10; } x1=x1/20*20; y1=y1/20*20; set_Qizi(x1,y1); m*=(-1);} d.按钮的触发事件 public void actionPerformed(ActionEvent e){ Graphics g=getGraphics(); if(e.getSource()==b1){ Game_start(); } else{ Game_re(); } if(e.getSource()==exist){ Game_re(); lblWin.setText(Get_qizi_color(color_Qizi)+“输了!”); intGame_Start=0; } e.判断落子的位置及画出相应的黑白棋子 public void set_Qizi(int x,int y)//落子{ if(intGame_Start==0)//判断游戏未开始{ return; } if(intGame_Body[x/20][y/20]!=0){ return; } Graphics g=getGraphics(); if(color_Qizi==1)//判断黑子还是白子{ g.setColor(Color.black); color_Qizi=0; } else{ g.setColor(Color.white); color_Qizi=1; } g.fillOval(x-10,y-10,20,20); intGame_Body[x/20][y/20]=color_Qizi+1;} f.判断胜负 if(Game_win_1(x/20,y/20))//判断输赢1{ lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0; } if(Game_win_2(x/20,y/20))//判断输赢2{ lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0;} if(Game_win_3(x/20,y/20))//判断输赢3{ lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”);intGame_Start=0;} if(Game_win_4(x/20,y/20))//判断输赢4{ lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0;} } 三、设计正文 3.1创建棋盘类 Public class WcyChess extends Applet ActionListener,MouseListener,MouseMotionListener,ItemListener{ int color_Qizi=0;//旗子的颜色标识 0:白子 1:黑子 int intGame_Start=0;//游戏开始标志 0未开始 1游戏中 int intGame_Body[][]=new int[16][16];//设置棋盘棋子状态 int m=-1;Button b1=new Button(“开始”);Button b2=new Button(“重置游戏”);Label lblWin=new Label(“ ”);Checkbox ckbHB[]=new Checkbox[3];Button exist = new Button(“退出”);CheckboxGroup ckgHB=new CheckboxGroup();NetchatClient chat=new NetchatClient();public void init(){ setLayout(null); addMouseListener(this); add(b1); b1.setBounds(330,50,80,30); b1.addActionListener(this); add(b2); b2.setBounds(330,90,80,30); b2.addActionListener(this); ckbHB[0]=new Checkbox(“执白”,ckgHB,false); ckbHB[0].setBounds(320,20,60,30); ckbHB[1]=new Checkbox(“执黑”,ckgHB,false); ckbHB[1].setBounds(380,20,60,30); ckbHB[2]=new Checkbox(“观看”,ckgHB, false); add(ckbHB[0]); add(ckbHB[1]); add(ckbHB[2]); ckbHB[0].addItemListener(this); ckbHB[1].addItemListener(this); add(lblWin); lblWin.setBounds(330,180,80,30); lblWin.setBackground(Color.red); lblWin.setText(“胜利者!”);//没有显示? ckbHB[2].setBounds(440, 20,60, 30); add(exist); exist.setBounds(330,130,80,30); implements // exist.addActionListener(this);add(chat);chat.setBounds(20, 500, 300, 300);chat.frame();chat.setVisible(true);Game_start_csh();setSize(500,600);setVisible(true);} public void itemStateChanged(ItemEvent e){ if(ckbHB[0].getState())//选择黑子还是白子{ color_Qizi=0;} else{ color_Qizi=1;} } public void mousePressed(MouseEvent e){} public void mouseClicked(MouseEvent e){ Graphics g=getGraphics();int x1,y1;x1=e.getX();y1=e.getY();if(e.getX()<20 || e.getX()>300 || e.getY()<20 || e.getY()>300){ return;} if(x1%20>10){ x1+=10;} if(y1%20>10){ y1+=10;} x1=x1/20*20;y1=y1/20*20;set_Qizi(x1,y1);m*=(-1);} public void actionPerformed(ActionEvent e){ Graphics g=getGraphics();if(e.getSource()==b1){ Game_start();} else { Game_re();} } if(e.getSource()==exist){ Game_re(); lblWin.setText(Get_qizi_color(color_Qizi)+“输了!”); intGame_Start=0;} } public void mouseEntered(MouseEvent e){} public void mouseExited(MouseEvent e){} public void mouseReleased(MouseEvent e){} public void mouseDragged(MouseEvent e){} public void mouseMoved(MouseEvent e){} public void paint(Graphics g){ draw_qipan(g); 3.2判断输赢功能实现 if(Game_win_1(x/20,y/20)){ //判断输赢1 lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0;} if(Game_win_2(x/20,y/20)){ //判断输赢2 lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0;} if(Game_win_3(x/20,y/20))//判断输赢3{ lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0;} if(Game_win_4(x/20,y/20))//判断输赢4{ lblWin.setText(Get_qizi_color(color_Qizi)+“赢了!”); intGame_Start=0;} 3.3 测试结果 a.进入游戏界面 游戏开始的界面有三个选择项,用户可以选择相应的角色,选择 完毕后点击开始进入游戏。 b.选择角色,开始下棋 首先达到五个棋子连在一块的赢了,并在红色区域显示谁赢了!c.下完后,重新开始 下完一盘后游戏停止,点击重新开始,界面回到初始界面,选择角色继续游戏.d.游戏中点退出,自动判输 在下棋的过程当中谁中途退出,即点击退出,系统自动判断谁输 四、课程设计心得体会 通过此次课程设计,将我本学期所学的JAVA知识得到巩固和应用,在设计的过程中我遇到了很到问题,不过在老师和同学们的帮助和自己的思考下还是很好的完成了。这此课程设计还让我懂得了写程序不能闭门造车,要努力拓宽知识面,开阔视野,拓展思维。它还让我学会了在网上查阅那些无限的资料。由于自己的分析设计和程序经验不足,该系统设计和实现过程中,还有许多没有完善的地方,比如用户界面设计不够美观,异常出错处理比较差等多方面问题,这些都有待进一步完善和提高。对于文中出现的不足和系统中出现的问题敬请老师指导。 五、参考文献 1.吴其庆编著.Java程序设计实例教程.北京:冶金工业出版社 2.柳西玲.许斌编著.Java语言应用开发基础.北京:清华大学出版社 3.丁振凡 Java 语言实用教程 :北京邮电大学出版社 附录(源代码) import java.net.*;import java.io.*;import java.applet.*;import java.awt.*;import java.awt.event.*;import java.applet.Applet;import java.awt.Color;Public class wuziqi extends Applet implements ActionListener,MouseListener,MouseMotionListener,ItemListener{ int color_Qizi=0;//旗子的颜色标识 0:白子 1:黑子 int intGame_Start=0;//游戏开始标志 0未开始 1游戏中 int intGame_Body[][]=new int[16][16];//设置棋盘棋子状态 int m=-1;Button b1=new Button(“开始”);Button b2=new Button(“重新开始”);Label lblWin=new Label(“ ”);Checkbox ckbHB[]=new Checkbox[3];Button exist = new Button(“退出”);CheckboxGroup ckgHB=new CheckboxGroup();public void init(){ setLayout(null); addMouseListener(this); add(b1); b1.setBounds(330,50,80,30); b1.addActionListener(this); add(b2); b2.setBounds(330,90,80,30); b2.addActionListener(this); ckbHB[0]=new Checkbox(“执白”,ckgHB,false); ckbHB[0].setBounds(320,20,60,30); ckbHB[1]=new Checkbox(“执黑”,ckgHB,false); ckbHB[1].setBounds(380,20,60,30); ckbHB[2]=new Checkbox(“观看”,ckgHB, false); add(ckbHB[0]); add(ckbHB[1]); add(ckbHB[2]); ckbHB[0].addItemListener(this); ckbHB[1].addItemListener(this); add(lblWin); lblWin.setBounds(330,180,80,30); lblWin.setBackground(Color.red); e.getY()<20 || e.getY()>300){ lblWin.setText(“胜利者!”);//没有显示? ckbHB[2].setBounds(440, 20,60, 30);add(exist); exist.setBounds(330,130,80,30);exist.addActionListener(this);Game_start_csh();setSize(500,600);setVisible(true);} public void itemStateChanged(ItemEvent e){ if(ckbHB[0].getState())//选择黑子还是白子 { color_Qizi=0;} else { color_Qizi=1;} } public void mousePressed(MouseEvent e){} public void mouseClicked(MouseEvent e){ Graphics g=getGraphics();int x1,y1;x1=e.getX();y1=e.getY(); if(e.getX()<20 || e.getX()>300 || return;} if(x1%20>10){ x1+=10;} if(y1%20>10){ y1+=10;} x1=x1/20*20;y1=y1/20*20;set_Qizi(x1,y1);m*=(-1)} public void actionPerformed(ActionEvent e){ Graphics g=getGraphics();if(e.getSource()==b1){ Game_start(); } else{ // 输了!“); 赢了!”); Game_re();} if(e.getSource()==exist){ Game_re(); color_Qizi=m; lblWin.setText(Get_qizi_color(color_Qizi)+“ intGame_Start=0; } } public void mouseEntered(MouseEvent e){} public void mouseExited(MouseEvent e){} public void mouseReleased(MouseEvent e){} public void mouseDragged(MouseEvent e){} public void mouseMoved(MouseEvent e){} public void paint(Graphics g){ draw_qipan(g);} public void set_Qizi(int x,int y){ //落子 if(intGame_Start==0){//判断游戏未开始 return;} if(intGame_Body[x/20][y/20]!=0){ return;} Graphics g=getGraphics(); if(color_Qizi==1){//判断黑子还是白子 g.setColor(Color.black); color_Qizi=0;} else{ g.setColor(Color.white); color_Qizi=1;} g.fillOval(x-10,y-10,20,20); intGame_Body[x/20][y/20]=color_Qizi+1;if(Game_win_1(x/20,y/20)){ //判断输赢1 lblWin.setText(Get_qizi_color(color_Qizi)+” intGame_Start=0; 了!“); 赢了!”); 赢了!“); 15*15 } if(Game_win_2(x/20,y/20)){ //判断输赢2{ lblWin.setText(Get_qizi_color(color_Qizi)+”赢 intGame_Start=0;} if(Game_win_3(x/20,y/20)){ //判断输赢3 lblWin.setText(Get_qizi_color(color_Qizi)+“ intGame_Start=0;} if(Game_win_4(x/20,y/20)){ //判断输赢4 lblWin.setText(Get_qizi_color(color_Qizi)+” intGame_Start=0;} } public String Get_qizi_color(int x){ if(x==0){ return “黑子”;} else { return “白子”;} } public void draw_qipan(Graphics G){ //画棋盘 G.setColor(Color.lightGray); G.fill3DRect(10,10,300,300,true);G.setColor(Color.black);for(int i=1;i<16;i++){ G.drawLine(20,20*i,300,20*i); G.drawLine(20*i,20,20*i,300);} } public void Game_start(){ //游戏开始 intGame_Start=1; Game_btn_enable(false); b2.setEnabled(true);} public void Game_start_csh(){//游戏开始初始化 intGame_Start=0; Game_btn_enable(true); b2.setEnabled(false); ckbHB[0].setState(true); for(int i=0;i<16;i++){ for(int j=0;j<16;j++){ intGame_Body[i][j]=0; } } lblWin.setText(“");} public void Game_re(){ //重新开始游戏 repaint(); Game_start_csh();} public void Game_btn_enable(boolean e){ //设置组件状态 b1.setEnabled(e); b2.setEnabled(e); ckbHB[0].setEnabled(e); ckbHB[1].setEnabled(e);} public boolean Game_win_1(int x,int y){ //横向判断输赢 int x1,y1,t=1; x1=x; y1=y; for(int i=1;i<5;i++){ if(x1>15){ break; } if(intGame_Body[x1+i][y1]==intGame_Body[x][y]){ t+=1; } else{ break; } } for(int i=1;i<5;i++){ if(x1<1){){ t+=1; } else{ break; } } if(t>4){ return true; } else{ return false; } } public boolean Game_win_2(int x,int y){ //纵向判断输赢 int x1,y1,t=1; x1=x; y1=y; for(int i=1;i<5;i++){ if(x1>15){ break; } if(intGame_Body[x1][y1+i]==intGame_Body[x][y]){ t+=1; } else{ break; } } for(int i=1;i<5;i++){ if(x1<1){ break; } if(intGame_Body[x1][y1-i]==intGame_Body[x][y]){ t+=1; } break; } if(intGame_Body[x1-i][y1]==intGame_Body[x][y] else{ break; } } if(t>4){ return true; } else{ return false; } } public boolean Game_win_3(int x,int y){ //左斜判断输赢 int x1,y1,t=1; x1=x; y1=y; for(int i=1;i<5;i++){ if(x1>15){ break; } if(intGame_Body[x1+i][y1-i]==intGame_Body[x][y]){ t+=1; } else{ break; } } for(int i=1;i<5;i++){ if(x1<1){ break; } if(intGame_Body[x1-i][y1+i]==intGame_Body[x][y]){ t+=1; } else{ break; } } if(t>4){ return true; } else{ return false; } } public boolean Game_win_4(int x,int y){ //左斜判断输赢 int x1,y1,t=1; x1=x; y1=y; for(int i=1;i<5;i++){ if(x1>15){ break; } if(intGame_Body[x1+i][y1+i]==intGame_Body[x][y]){ t+=1; } else{ break; } } for(int i=1;i<5;i++){ if(x1<1){ break; } if(intGame_Body[x1-i][y1-i]==intGame_Body[x][y]){ t+=1; } else{ break; } } if(t>4){ return true; } else{ return false; } } } 姓 名: 刘旭 学 院: 计算机与通信学院 班 级: 通信工程101班 指导老师: 文志诚 目录 一、需求分析..................................................................................................................................3 1.1 开发背景....................................................................................................................................3 2.2 功能简介....................................................................................................................................3 二、系统设计..................................................................................................................................4 2.1 函数一览....................................................................................................................................4 2.2 “封面”的设计........................................................................................................................4 2.3 二维数组与控制台....................................................................................................................5 2.4 键盘操作....................................................................................................................................6 2.5判定.............................................................................................................................................7 2.6 悔棋的实现................................................................................................................................8 三、调试运行..................................................................................................................................9 3.1 进入界面....................................................................................................................................9 3.2 棋盘的初始状态......................................................................................................................10 3.3 激战中……..............................................................................................................................10 3.4 游戏结束..................................................................................................................................11 四、解决问题的关键....................................................................................................................11 五、课设总结................................................................................................................................11 六、附录........................................................................................................................................12 6.1 画图代码..................................................................................................错误!未定义书签。6.2 初始化......................................................................................................错误!未定义书签。6.3 Play函数..................................................................................................错误!未定义书签。 一、需求分析 1.1 开发背景 学习了数据结构该门课程,对于枯燥无味的理论知识,我们是否能够通过所学的知识在课程设计中做出有趣味东西,然后让我们对于数据结构更加的感兴趣呢?于是我和我的室友陈明建开始酝酿着写些什么东西。上个学期就已经写了通讯录那之类的链式结构,这次我们决心有所改变,我们学习了栈、队列、树、图,字典树有人选了,我们就来写一个基于图的小程序,五子棋,对,图的简单应用,于是我们开始着手来写这个小小的程序,祝我们好运! 2.2 功能简介 既然是五子棋,我们要做的是时时刻刻的将整个图(以下称为棋局)的状态呈现出来,那么界面就是必不可少的。MFC不会?没关系,我们就用基于控制台的字符输出来构建这个棋局吧,当然这只是第一步,详细如下: 1拥有一个良好的进入界面,以及必要的选项; ○2拥有一个二维的数组来记录和更新实时的状态,并且能够有一种方法在DOS界面下绘制○出整个棋局的实时状态(包括棋盘和棋子); 3能够通过键盘上的按键完成所选位置的移动和选定操作; ○4能够在每一次的走棋后判定是否游戏结束(棋盘走满或者是一方胜出); ○5能够完成悔棋的功能,并保证这之间的棋局绘图能够与二维数组数据同步,做到真正意○义上的悔棋。 二、详细设计 2.1 函数一览 2.2 “封面”的设计 首先还是讲些题外话,该程序由于与控制台有密切的关系,于是在代码中使用了不少 conio.h 中的函数,当然在显示时又使用了windows.h 中的 Sleep()函数,正是有了这些函数的使用,程序才得以顺利完成,尤其是后面频繁使用的gotoxy()函数。 进入正题,由于是一个小的程序,因此将每一个功能分成一个一个的函数,这样将在以后的修改和完成进度上都有很大的帮助。由上面的函数一览可以知道这个“封面”就是在Logo()函数里面实现的,函数实现过程中使用了Sleep()函数,使之有动态效果: void Logo(){ char Wel[30]= { “Made By Lyush&& Mirs Chen” }; printf(“ttt 欢迎试用五子棋系统n”); printf(“tt ”); for(int i= 0;i< strlen(Wel);++i) { putchar(Wel[i]); Sleep(200);// 可使字符一个一个的输出 } putchar(10);// 换行对应的 ASCII 码值为十进制的 10 } 2.3 二维数组与控制台 二维数组是用来使得整个棋盘的信息全部记录下来,因此在结构体中二维数组的声明是最关键的。 struct { int Status[MAX/2+2][MAX/2+2]; int MINBOX; int Step; char Graph[3][3]; char *FillGraph[9]; Sta Stack;} ChessBoard; 声明全局变量是为了使得各函数能够更方便地使用到这个结构体,现假设某点的坐标为(1, 1),那么如何在屏幕上打印这个点呢?这就利用到了ChangeCoordinates()与gotoxy()函数,前者使坐标进行转换,后者让光标走到所指的那个点,其实主要还是因为类似“┣、╋、●、○”在横向上所占都是两个英文字母的距离,因此在控制台上反映的就是和数组下标倍数关系了。部分代码如下: HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE); void ChangeCoordinates(int _X, int _Y, int *X, int *Y){ *X=(_X-1)* 2; *Y=(_Y-1)* 4;} void gotoxy(int x, int y)//这是光标的函数 { COORD coord; coord.Y= x; // 在实际的应用过程中发现交换x与y的赋值 coord.X= y; // 更好理解,即横行位x,纵行为y。 SetConsoleCursorPosition(hConsole, coord); } 2.4 键盘操作 在刚开始写这个五子棋的时候是以坐标来确定玩家的每一步棋,但后来发现这样操作性实在是差,键盘操作是更好的选择。这里又要用到一个函数 getch(),其作用是无回显的接受从键盘输入的字符,让屏幕不会出现你输入的字符且等待着按回车确定…… 有了这个宝贝函数,马上得到 “↑” 对应的ASCII码为-32和72 两个连着的数值,依次可得其他对应的ASCII码。后面在使玩家一和玩家二分离操作,玩家一则是利用 W、S、A、D + space来操作,玩家二则是 上下左右+ enter。配合ChangeCoordinates()与gotoxy()函数,完成对走棋的控制。部分代码如下: if(Opreat[0]== 13&& Ply== 2|| Opreat[0]== 32&& Ply== 1){ if(ChessBoard.Status[Move_X][Move_Y]== 0) { int TTop= ++ChessBoard.Stack.Top;ChessBoard.Status[Move_X][Move_Y]= Ply;ChessBoard.Stack.Record[TTop][0]= Move_X;ChessBoard.Stack.Record[TTop][1]= Move_Y;printf(“%s”, Graph);return true;// 该次走棋操作有效 } else { … } } if(Opreat[0]==-32&& Opreat[1]== 72|| Opreat[0]== 'w'|| Opreat[0]== 'W'){// 凡是接受了“上操作”,则Move_X的值减一,if(Currect(Move_X-1, Move_Y)) { Move_X-= 1; } } else if(…){ … } // 这是接下来的转换操作 ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y);Gotoxy(Temp_X, Temp_Y); 2.5判定 对于每次走棋后,首先应该做的就是判定一否有五个棋子已经连成一线,也是一个简单的搜索过程,由于每次走的点不一定是最外部的点,因此从每次走的点的两头同时搜索,当遇到两端同时结束时,搜索结束。当满足五子时游戏结束。当然,当棋盘被走满时,游戏亦结束。代码如下: bool Legal(int Point){ if(Point< 1|| Point> MAX/ 2+ 1) return false; else return true;} //搜索45度角是否为满足ChessBoard.MINBOX 以X正轴为参考轴 if(!Flag){ Count= 1;for(int i1= X-1, j1= Y+ 1, i2= X+ 1, j2= Y-1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1++, i2++, j2--){ int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX){ Flag= 1; return true; } } } 2.6 悔棋的实现 虽说下棋悔棋是一种不道义的行为,但是如果双方约定好了,未尝不可。在没写悔棋之前,只是记录了“上一次”的位置,声明了Last_X,Last_Y;当然既然要求悔棋,那么直接调用栈顶元素,即可定位上次走棋的位置。那么悔棋呢,取出“上一次”的位置,判定位置(不同的位置对应不同的填充图形类型)在二维数组中撤销走棋时所赋予的 Ply 值(玩家一走时,其值为1,玩家二走时,其值为2),重新将 ChessBoard.Status[ Last_X ][ Last_Y ] 赋为0。代码如下: int GetFillType(int X, int Y){ if(X== 1) { if(Y== 1) return 0; else if(Y== 16) return 2; else return 1; } else if(X== 16) { if(Y== 1) return 6; else if(Y== 16) return 8; else return 7; } else { if(Y== 1) return 3; else if(Y== 16) return 5; else return 4; } } bool Retract(int *X, int *Y){ int Temp_X, Temp_Y, TTop, FillType; if(!StackEmpty()) { TTop= ChessBoard.Stack.Top--; *X= ChessBoard.Stack.Record[TTop][0]; *Y= ChessBoard.Stack.Record[TTop][1]; ChessBoard.Status[*X][*Y]= 0;// 将该点置为真正意义上的空点 FillType= GetFillType(*X, *Y); ChangeCoordinates(*X, *Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); printf(“%s”, ChessBoard.FillGraph[FillType]); return true; } else { Gotoxy(9, 65); printf(“您已不能悔棋”); Sleep(300); Gotoxy(9, 65); printf(“ ”); return false; } } 三、调试运行 3.1 进入界面 3.2 棋盘的初始状态 3.3 激战中…… 3.4 游戏结束 四、解决问题的关键 这个五子棋的程序并没有什么复杂的算法,只是利用了简单的图知识和一个栈的应用,在这里主要的关键问题就是如何将程序有条理的写下来,有一个好的逻辑思维。将程序分成了多个功能函数,尽量的让一个函数的功能单一,只是在内部调用了其他的函数以辅助改函数功能的实现,比如判定坐标是否越界,坐标是否合法,悔棋的点的位置状态…… 这样便能做到各个击破,程序的形成也就变得畅通许多了。 五、课设总结 刚开始写这个程序,认为一定要用到 graphics.h, 无奈电脑TC不兼容,因此只好强行来画这个界面了,使用输入法里面的制表符,效果还不错,通过一长串的if … else … 最好还是画出来了,这个时候觉得控制台的简单图形还是能够画出来的,并且可以尽量去美化它的界面。后面的附录中将给出画棋盘和棋子的源代码。在程序设计的过程中,尤其是为源程序加上悔棋的功能,这期间总是有许多意想不到的错误,比如加上后,有时走了5个连子棋,但是程序并没有判定输赢,而是可以继续走、有时没有五个却已经结束了,光标没有复位,悔棋后,玩家的走棋顺序没有跟着改变……通过后来的一步步修改终于使得这些问题都一一解决了,比如说对 Prompt(提示)函数引进了返回值,判断该次操作是否成功,如果下了棋则为 true,如果是悔棋就是 false 了,这样便使得后面的操作更规范了和统一了。 六、附录 #include HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE); void HideCursor(){ CONSOLE_CURSOR_INFO cursor_info = {1, 0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);} typedef struct { int Record[260][2]; int Base; int Top;} Sta; struct { int Status[MAX/2+2][MAX/2+2]; int MINBOX; int Step; char Graph[3][3]; char *FillGraph[9]; Sta Stack; } ChessBoard; void Gotoxy(int x, int y)//这是光标的函数 { COORD coord; coord.Y= x; coord.X= y; SetConsoleCursorPosition(hConsole, coord); } void Logo(){ char Wel[30]= { “Made By Lyush&& Mirs Chen” }; printf(“ttt 欢迎试用五子棋系统n”); printf(“tt ”); for(int i= 0;i< strlen(Wel);++i) { putchar(Wel[i]); Sleep(200); } putchar(10);} int Login(){ int Mode, Skip= 0; char Request; if(!Skip) { printf(“nn在这儿你能DIY(Do it youself!)你的棋子,每个棋子接受一个汉字”); printf(“ Y Orz Nn”); scanf(“%c”, &Request); if(Request== 'Y'|| Request== 'y') { printf(“玩家一的 DIY 棋子-->”); scanf(“%s”, ChessBoard.Graph[1]); ChessBoard.Graph[1][2]= ' '; printf(“玩家二的 DIY 棋子-->”); scanf(“%s”, ChessBoard.Graph[2]); ChessBoard.Graph[2][2]= ' '; } } printf(“nn请选择先手玩家:___ nn”); printf(“nntttttt1 对应 玩家一对应 玩家二n”); if(Request== 'Y'|| Request== 'y') Gotoxy(10, 16);//原函数是 第一个参数为列,后一个参数为行,把Gotoxy函数做了更改 else Gotoxy(8, 16); scanf(“%d”, &Mode); if(Mode!= 1&& Mode!= 2) return Mode% 2+ 1; else return Mode;} void InitChessBiard(){ int TTop= ChessBoard.Stack.Top; fflush(stdin); ChessBoard.Step= 0; ChessBoard.Stack.Top= 0; ChessBoard.Stack.Base= 0; ChessBoard.Stack.Record[TTop][0]= 8;// 栈的0号位存储初始化的棋盘位置 ChessBoard.Stack.Record[TTop][1]= 8; ChessBoard.MINBOX= 5; ChessBoard.FillGraph[0]=“┏”; ChessBoard.FillGraph[1]=“┳”; ChessBoard.FillGraph[2]=“┓”; ChessBoard.FillGraph[3]=“┣”; ChessBoard.FillGraph[4]=“╋”; ChessBoard.FillGraph[5]=“┫”; ChessBoard.FillGraph[6]=“┗”; ChessBoard.FillGraph[7]=“┻”; ChessBoard.FillGraph[8]=“┛”; strcpy(ChessBoard.Graph[1], “○”); strcpy(ChessBoard.Graph[2], “●”); memset(ChessBoard.Status, 0, sizeof(ChessBoard.Status));} bool Legal(int Point){ if(Point< 1|| Point> MAX/ 2+ 1) return false; else return true;} bool Currect(int X, int Y){ if(Legal(X)&& Legal(Y)) return true; else return false;} void ChangeCoordinates(int _X, int _Y, int *X, int *Y){ *X=(_X-1)* 2; *Y=(_Y-1)* 4;} void Draw(){ // 画棋盘 for(int i= 1;i<= MAX;++i) { for(int j= 1;j<=MAX;++j) { if(i== 1) { if(j== 1) printf(“┏”); else if(j== MAX) printf(“┓n”); else if(j%2) printf(“┳”);// 横向占两个坐标位,竖向占一个坐标位 else printf(“━”); } else if(i== MAX) { if(j== 1) printf(“┗”); else if(j== MAX) printf(“┛n”); else if(j%2) printf(“┻”); else printf(“━”); } else { if(j== 1) { if(i% 2) printf(“┣”); else printf(“┃”); } else if(j== MAX) { if(i% 2) printf(“┫n”); else printf(“┃n”); } else { if(i% 2) { if(j% 2) printf(“╋”); else printf(“━”); } else { if(j% 2) printf(“┃”); else printf(“ ”); } } } } } // 画棋子 for(int i= 1;i<= MAX/ 2+ 1;++i) { for(int j= 1;j<= MAX/ 2+ 1;++j) { int Temp_X, Temp_Y; ChangeCoordinates(i, j, &Temp_X, &Temp_Y); if(ChessBoard.Status[i][j]== 1) { Gotoxy(Temp_X, Temp_Y); printf(“○”); } else if(ChessBoard.Status[i][j]== 2) { Gotoxy(Temp_X, Temp_Y); printf(“●”); } } } } int GetFillType(int X, int Y){ if(X== 1) { if(Y== 1) return 0; else if(Y== 16) return 2; else return 1; } else if(X== 16) { if(Y== 1) return 6; else if(Y== 16) return 8; else return 7; } else { if(Y== 1) return 3; else if(Y== 16) return 5; else return 4; } } bool StackEmpty() { if(ChessBoard.Stack.Top== ChessBoard.Stack.Base) return true; else return false;} bool Retract(int *X, int *Y){ int Temp_X, Temp_Y, TTop, FillType; if(!StackEmpty()) { TTop= ChessBoard.Stack.Top--; *X= ChessBoard.Stack.Record[TTop][0]; *Y= ChessBoard.Stack.Record[TTop][1]; ChessBoard.Status[*X][*Y]= 0;// 将该点置为真正意义上的空点 FillType= GetFillType(*X, *Y); ChangeCoordinates(*X, *Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); printf(“%s”, ChessBoard.FillGraph[FillType]); return true; } else { Gotoxy(9, 65); printf(“您已不能悔棋”); Sleep(300); Gotoxy(9, 65); printf(“ ”); return false; } } bool Prompt(int Ply, int Last_X, int Last_Y){ int Move_X= Last_X, Move_Y= Last_Y; int Temp_X, Temp_Y; char Opreat[2]; char *Graph= ChessBoard.Graph[Ply]; Gotoxy(1, 65); printf(“按退格键悔棋”); Gotoxy(3, 65); if(Ply== 1) { printf(“玩家一走棋:”); Gotoxy(5, 65); printf(“通过w s a d”); } else { printf(“玩家二走棋:”); Gotoxy(5, 65); printf(“通过↑↓←→”); } Gotoxy(7, 65); printf(“按空格或回车”); ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); while(1) { Opreat[0]= getch(); if(Opreat[0]== 8) { if(Retract(&Move_X, &Move_Y)) return false;// 该次操作为伪操作 else { Gotoxy(Temp_X, Temp_Y); continue; } } else { if(Opreat[0]== 13&& Ply== 2|| Opreat[0]== 32&& Ply== 1) { if(ChessBoard.Status[Move_X][Move_Y]== 0) { int TTop= ++ChessBoard.Stack.Top; ChessBoard.Status[Move_X][Move_Y]= Ply; ChessBoard.Stack.Record[TTop][0]= Move_X; ChessBoard.Stack.Record[TTop][1]= Move_Y; printf(“%s”, Graph); return true;// 该次走棋操作有效 } else { Gotoxy(9, 65); printf(“此步无效”); Sleep(300); Gotoxy(9, 65); printf(“ ”); Gotoxy(Temp_X, Temp_Y); continue; } } if(Ply== 2) { if(Opreat[0]!=-32) continue; Opreat[1]= getch(); } if(Opreat[0]==-32&& Opreat[1]== 72|| Opreat[0]== 'w'|| Opreat[0]== 'W') { if(Currect(Move_X-1, Move_Y)) { Move_X-= 1; } } else if(Opreat[0]==-32&& Opreat[1]== 80|| Opreat[0]== 's'|| Opreat[0]== 'S') { if(Currect(Move_X+ 1, Move_Y)) { Move_X+= 1; } } else if(Opreat[0]==-32&& Opreat[1]== 75|| Opreat[0]== 'a'|| Opreat[0]== 'A') { if(Currect(Move_X, Move_Y-1)) { Move_Y-= 1; } } else if(Opreat[0]==-32&& Opreat[1]== 77|| Opreat[0]== 'd'|| Opreat[0]== 'D') { if(Currect(Move_X, Move_Y+ 1)) { Move_Y+= 1; } } ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); } } } bool Win(int Ply, int X, int Y) { // 先找上下的是否存在一条直线连成ChessBoard.MINBOX个 int Count= 1, Flag= 0; for(int i= X-1, k= X+ 1;Legal(i)|| Legal(k);i--, k++) { int LastCount= Count; if(Legal(i)&& ChessBoard.Status[i][Y]== Ply) { Count++; } if(Legal(k)&& ChessBoard.Status[k][Y]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } // 左右查找是否满足条件 if(!Flag) { Count= 1; for(int i= Y-1, k= Y+ 1;Legal(i)|| Legal(k);i--, k++) { int LastCount= Count; if(Legal(i)&& ChessBoard.Status[X][i]== Ply) { Count++; } if(Legal(k)&& ChessBoard.Status[X][k]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } } //搜索45度角是否为满足ChessBoard.MINBOX 以X正轴为参考轴 if(!Flag) { Count= 1; for(int i1= X-1, j1= Y+ 1, i2= X+ 1, j2= Y-1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1++, i2++, j2--) { int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } } // 搜索135度角是否满足ChessBoard.MINBOX if(!Flag) { Count= 1; for(int i1= X-1, j1= Y-1, i2= X+ 1, j2= Y+ 1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1--, i2++, j2++) { int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } } return false;} void Play(int Fir){ system(“cls”); Draw(); //SetConsoleTextAttribute(hConsole, FOREGROUND_RED| FOREGROUND_INTENSITY); int CurPly=Fir; while(1) { int TTop= ChessBoard.Stack.Top; if(ChessBoard.Step>= 256) { Gotoxy(11,65); printf(“游戏结束”); } if(Prompt(CurPly, ChessBoard.Stack.Record[TTop][0], ChessBoard.Stack.Record[TTop][1])) { TTop= ChessBoard.Stack.Top; if(Win(CurPly, ChessBoard.Stack.Record[TTop][0], ChessBoard.Stack.Record[TTop][1])) { Gotoxy(11, 65); if(CurPly== 1) printf(“玩家一胜利n”); else printf(“玩家二胜利n”); break; } CurPly= CurPly% 2+1; ChessBoard.Step++; } else if(ChessBoard.Step) { CurPly= CurPly% 2+1; ChessBoard.Step--; } } } char Reset(){ char Decide; Gotoxy(15, 65); printf(“是否重玩?”); Gotoxy(17, 65); printf(“'y' Orz 'n'n”); Gotoxy(19, 65); Decide= getchar(); return Decide;} int main(){ system(“mode con cols=80 lines=33”); Loop : //system(“color 2f”); InitChessBiard(); Logo(); Play(Login()); if(Reset()== 'Y'|| Reset()== 'y') { system(“cls”); goto Loop; } system(“pause”); return 0;}第二篇:五子棋游戏软件工程课程设计
第三篇:五子棋游戏项目报告
第四篇:五子棋JAVA语言课程设计报告
第五篇:数据结构课程设计-五子棋