C语言图形五子棋课程设计报告.docx
北京师范大学 C语言课程设计报告 课题名称: 游戏五子棋 指导教师: 尹乾 课题组员: 罗福莉 赵帅帅 何虹达 院系: 信息科学与技术 时间: 2014.3.15-2014.4.20 摘 要五子棋是一种两人对弈的纯策略型棋类游戏,应用C语言编写程序可以在计算机上实现二人对弈五子棋功能。二人对弈五子棋程序由欢迎界面显示、游戏界面生成、光标移动与落子、判断胜负、悔棋功能、提供音效等子程序构成;程序中应用了结构体、数组、全局变量、按键处理和图形编程等元素和语句。程序通过棋盘和棋子图像生成、二人移子与落子和判断胜负等功能的实现,在计算机上实现了二人五子棋对弈。 目 录摘 要2第1章:需求分析31.1五子棋背景31.2 五子棋需求分析和流程设计4第2章:概要设计72.1 各类头文件和全局变量72.2 画面显示模块8第3章:详细设计113.1 玩家操作模块113.2音效提供模块113.3 胜负判断模块12第4章:调试分析134.1 图形模块13 4.2 玩家操作模块134.3 胜负判断模块14第5章:用户手册14第6章:小组分工15第7章:结论与心得16第8章 :源程序代码16附录一 Color命令的使用说明29 附录二 ASCII 码表29第1章:需求分析1.1五子棋背景传统五子棋的棋具与围棋相同,棋子分为黑白两色,棋盘为18×18,棋子放置于棋盘线交叉点上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 因为传统五子棋在落子后不能移动或拿掉,所以也可以用纸和笔来进行游戏。1.2 五子棋需求分析和流程设计本程序设计为人与人对弈,一方执黑棋,一方执白棋,轮流走棋,每方都试图在游戏结束前让自己的棋子五子相连,首先实现五子相连的一方获胜。程序执行过程中,要求棋盘、棋子时时可见,游戏界面有提示信息轮到何方下棋,人可以通过按键盘按键移动光标,再点击enter键摆放棋子,并且每落一子都有系统声音,创新之处是可以提供悔棋功能。1.2.1 程序需求分析根据功能需求,将程序分为画面显示、玩家操作、音效提供、胜负判断五个模块,以下分析各模块的需求。画面显示模块:程序开始运行时,显示制作者和给出欢迎及退出界面;游戏开始后要求生成18×18的棋盘图像,并在棋盘上方显示欢迎信息“欢迎play our五子棋”,棋盘下方游戏显示应该轮到甲方或者乙方落子,棋盘左右显示双方操作方式,进行过程中,要求实时显示棋盘上已落下的棋子,甲方为白球,乙方为白圈;分出胜负后,要求给出游戏结束画面,并且询问用户是否需要继续游戏。玩家操作模块:程序开始时,需玩家确定选择“人人对战”后开始游戏;游戏过程中,两个玩家通过不同的按键移动光标,选择落子;游戏进行过程中,当前玩家下棋后,另一位玩家下棋前,当前玩家可以悔棋。悔棋提示在棋盘右下方,为按键“b”。游戏结束时,有玩家选择是否开始新游戏。 音效提供模块:玩家每落一子,系统提供音效一声,增加下棋的趣味性。胜负判断模块:实时监测棋盘上棋子,一旦某一色棋子出现五子连线,终止游戏程序,并着色连成一线的五子,棋盘下方弹出该色玩家胜出信息。1.2.2程序流程设计 根据程序需求分析结果,可以得出程序的总体结构图如图1,程序总体流程图如图2。五子棋游戏五子棋模块二:玩家操作模块三:音效提供模块四:胜负判断模块一:画面显示 图1 五子棋总体结构图开始制作者展示进入菜单界面N人人对战?退出游戏Y是否继续新游戏?开始游戏ESCY确认退出?b悔棋甲方判断NEnter甲方获胜?退出游戏显示甲方获胜消息Y确认退出?N乙方判断ESCb悔棋YEnterN显示乙方获胜消息乙方获胜?YN 图2 程序总体流程图第2章:概要设计2.1 各类头文件和全局变量#include <stdio.h>#include<windows.h>#include <stdlib.h>#include<conio.h>/使用getch()函数int startchoice; /int winner; /int player; /Q200200= 0;/Q数组记录旗子char button;/读入键盘输入的指令,如2.2 画面显示模块画面显示模块由欢迎界面,游戏棋盘界面,确认是否退出对话框界面,以及感谢使用界面组成。 画面显示模块函数如下:void Welcome() /欢迎界面void Draw() /绘画游戏棋盘界面void Clean()/清除运动的轨迹void menu_choose(char press)/选择是否退出游戏对话框void PutDown()/显示落子函数void goto_xy(int x, int y) /光标移动函数 画面效果图如图3,4,5,6。 图3 欢迎界面图 图4 主菜单界面图 图5 游戏界面图 图6 确认退出对话框第3章:详细设计3.1 玩家操作模块棋子的移动与落子有键盘上按键控制,本程序选取甲乙双方按键都为W、S、和回车键,“b”,“ESC”,分别代表上移、下移、左移、右移光标和落子,悔棋,退出。在光标移动的过程中,光标按照玩家按键移动;在玩家按下落子按键后,程序自动调用棋子显示子程序和判断胜负子程序。当前玩家下棋后,另一位玩家下棋前,当前玩家可以悔棋。悔棋提示在棋盘右下方,为按键“b”。甲乙的落子后,程序会为落子处的数组元素赋一个特定值(玩家甲的棋子赋为1,玩家乙的棋子赋为2),用于判定胜负和悔棋。玩家操作模块主要由以下函数构成:void Record() /记录棋子的情况void go_back(int x1,int y1)/悔棋函数void Play(char ch)/读取键盘的操作 移动光标 下棋和悔棋操作3.2音效提供模块为了提高游戏的趣味性,我们为本游戏提供了简单系统音效。程序语句很简单 printf("a");/'a'表示蜂鸣声3.3 胜负判断模块胜负判断模块是程序的关键,该模块的设计直接关系到程序的运行速率和运行结果的正确与否。本函数根据每次落子的位置,分别向上、下、左、右、左上、左下、右上、右下八个方向判断是否有相同颜色的棋子连成五子,如果成立,游戏就结束,并显示提示信息,否则继续落子。以下简析本程序流程:,由获胜条件可以知,通过判断行、列、斜边、反斜边方向上是否有连续的5个子即可得出是否获胜结果。在游戏开始时,将棋盘初始化,即将棋盘抽象为一个18*18的数组,数组中每个元素数值设为0。甲方落子时,将数组内相应坐标处元素赋值为2;乙方落子时,将数组内相应坐标处元素赋值为1。通过循环扫描棋盘数组,经扫描后,如发现在行、列、斜边、反斜边方向上有五个连续的2,甲方获胜;如发现在行、列、斜边、反斜边方向上有五个连续的1,乙方获胜。胜负判断模块主要由一个函数构成:int Judge() /判断胜负函数第4章:调试分析4.1 图形模块1在图形模块中,因为是第一次使用,所以刚开始时对于棋盘创建的位置把握有些欠缺。解决方案:参考书本以及google,找出最合适的位置坐标。2. 在构建棋子时候,发现C语言中似乎对颜色的处理有些错误,当棋谱线的颜色为白色时,无法构建黑色棋子,只能画出白色棋子。解决方案:用白圈区别于白球,类似白子和黑子。4.2 玩家操作模块1. 在玩家操作模块中,出现的最大问题之一就是棋盘已经有棋子的地方还可以覆盖另一个棋子。解决方案:通过在PutDown()函数中添加if条件语句,判定如果所在位置对应的数组值不等于0时,不能落子。2. 另一个问题就是之前提到的,操作定位框的时候会将定位框移到棋盘之外造成溢出。解决方案: 通过if语句判定,如果操作框超出范围则移动到对应相反位置,例如移动棋盘最上端,若继续向上移动,则移动至棋盘最下端对应位置。3. 还有一个就是悔棋模块中,通过将现有棋子覆盖与棋盘底色相同的颜色来覆盖后,棋谱线条部分也会被覆盖。解决方案:在go_back();即悔棋函数。用棋谱线来覆盖棋子(例如: 等)。4.3 胜负判断模块1. 在胜负判断中一直没能想到好的办法来数据化判断哪方玩家获得胜利。解决方案:通过参考书籍并加以优化,得出将棋谱做成数组,定义甲方落下为1,乙方落下为2,初始为0,这样即不会造成冲突,也很好的解决了判断问题。2. 另一个问题在于一直不能优化代码做到不每次都扫描整个棋盘来判断胜负。解决方案:至今尚未解决,尝试过局部扫描,但失败了。第5章:用户手册1.进入演示程序后,即显示欢迎界面,几秒后,按任何键进入菜单界面,再选择人人对战可以进入主界面开始游戏或者选择退出键。2.棋子的移动与落子有键盘上按键控制,本程序选取甲乙方都为、和回车键,分别代表上移、下移、左移、右移光标和落子。在光标移动的过程中,光标按照玩家按键移动;在玩家按下落子按键后,程序自动调用棋子显示子程序和判断胜负子程序。3. 在当前玩家按下落子按键后,程序自动调用棋子显示子程序和判断胜负子程序。当前玩家下棋后,另一位玩家下棋前,当前玩家可以悔棋。悔棋提示在棋盘右下方,为按键“b”(即backspace)。4.游戏中Esc键可以直接退出游戏。5.游戏过程中,如果玩家1或者玩家2有一方获得胜利后,程序自动将提示哪一方获得了胜利,并可以选择是否继续新游戏。6.游戏结束且玩家选择不再继续后,显示谢谢使用界面,再按任意键退出游戏。第6章:小组分工组长:罗福莉组员:赵帅帅,何虹达具体分工:组员任务分工罗福莉报告书写 程序调试 展示成果赵帅帅胜负判断设计 主函数设计何虹达界面制作 程序调试第7章:结论与心得 通过对各子程序的设计与优化,本程序完成了五子棋软件的主体的设计与制作,基本达到了使用五子棋软件的核心要求。然而程序还有一些不足之处,首先,程序的界面过于简陋,其次,判断胜后没有显示连成一线的棋子是哪些,用户使用不便。最后就是程序法实现人机对战,缺乏可玩性。 第8章 :源程序代码 /五子棋小游戏#include <stdio.h>#include<windows.h>#include <stdlib.h>#include<conio.h>/使用getch()函数int startchoice,winner,player,Q200200= 0;/Q数组记录旗子char button;/读入键盘输入的指令,如struct Point/点坐标的结构体 int x,y; point,game_xy;struct Piece/棋子的坐标记录 struct Point coord; struct Piece *fore;struct Piece *head,*p,*ptr,*ptr1;void goto_xy(int x, int y) /光标移动函数 COORD c; c.X=2*x; c.Y=y; SetConsoleCursorPosition (GetStdHandle(STD_OUTPUT_HANDLE), c);void Init()/初始化函数,将记录棋子的数组初始化 for(int i=0; i<200; i+) for(int j=0; j<200; j+) Qij=0; startchoice=0;player=1; p=(struct Piece *)malloc(sizeof(struct Piece); head=p;void Welcome() /欢迎界面system("color 2F");/#include<windows.h> 2 背景绿色 F 字体亮白色 goto_xy(10,3); printf(""); goto_xy(17,1); printf("主菜单"); goto_xy(13,3); printf(" *人人对战*"); goto_xy(13,4); printf(" *退出*"); point.x=12; point.y=3; goto_xy(0,0);void ShowWho()/显示轮到哪一方下棋 goto_xy(17,22); if(player=0) printf("轮到甲方落子"); else printf("轮到乙方落子"); goto_xy(point.x,point.y);void Draw() /绘画游戏界面game_xy.x=10; game_xy.y=3; system("cls"); system("color 3F");/3 湖蓝色 F 亮白色 goto_xy(15,1); printf("欢迎play our 五子棋!"); /*goto_xy(29,22); printf("重新开始 r");*/ goto_xy(1,22); printf("悔棋 b"); goto_xy(1,23); printf("退出 ESC");const int i=8;/const 定义的数据不可以被改变 而且修改数据比较方便 const int j=19;const int k=3; goto_xy(game_xy.x-i,game_xy.y+k);/输出甲方的下棋方法 printf("甲方: "); goto_xy(game_xy.x-i,game_xy.y+k+2); printf("移动: 上 "); goto_xy(game_xy.x-i,game_xy.y+k+4); printf(" 下 "); goto_xy(game_xy.x-i,game_xy.y+k+6); printf(" 左 "); goto_xy(game_xy.x-i,game_xy.y+k+8); printf(" 右 ");goto_xy(game_xy.x-i,game_xy.y+k+10);printf("落子: Enter");goto_xy(game_xy.x+j,game_xy.y+k);/输出乙方的下棋方法 printf("乙方: "); goto_xy(game_xy.x+j,game_xy.y+k+2); printf("移动: 上 "); goto_xy(game_xy.x+j,game_xy.y+k+4); printf(" 下 "); goto_xy(game_xy.x+j,game_xy.y+k+6); printf(" 左 "); goto_xy(game_xy.x+j,game_xy.y+k+8); printf(" 右 ");goto_xy(game_xy.x+j,game_xy.y+k+10);printf("落子: Enter"); for(int k1=0; k1<200; k1+)/初始化棋子记录,在第二局时有明确的作用 for(int k2=0; k2<200; k2+) Qk1k2=0; for(int i=0; i<18; i+)/画棋盘 if(i=0)/画第一行 goto_xy(10,i+3); printf(""); if(i!=0&&i!=17)/画出中间16行 goto_xy(10,i+3); printf(""); if(i=17)/画最后一行 goto_xy(10,i+3); printf(""); point.x=19; point.y=12; goto_xy(19,12);void Clean()/清除运动的轨迹 goto_xy(10,3); printf(" "); goto_xy(24,3); printf(" "); goto_xy(10,4); printf(" "); goto_xy(24,4); printf(" ");void menu_choose(char press)/选择游戏还是退出if(press=72)/的ASCLL码 if(point.y=3) point.y=4; else point.y=3; Clean(); goto_xy(10,point.y); printf(""); if(press=80)/的ASCLL码 if(point.y=4)point.y=3; elsepoint.y=4; Clean(); goto_xy(10,point.y); printf(""); if(press=13)/ 13:回车键的ASCLL码 startchoice=point.y-2;/startchoice 为1或2 void go_back(int x1,int y1)/悔棋函数 goto_xy(x1,y1); if(x1=10) if(y1=3) printf(""); else if(y1=20) printf(""); else printf(""); else if(x1=27) if(y1=3) printf(""); else if(y1=20) printf(""); else printf(""); else if(y1=3) printf(""); else if(y1=20) printf(""); else printf(""); Qpoint.xpoint.y=0;/在数组中将弹出的棋子对应的数据设为0 goto_xy(x1,y1);void Record() /记录棋子的情况 p->coord.x=point.x; p->coord.y=point.y; ptr=p; p=(struct Piece *)malloc(sizeof(struct Piece); p->fore=ptr; ShowWho(); Qpoint.xpoint.y=player+1; if(player) player=0; return; player=1; goto_xy(point.x,point.y);void PutDown()/显示落子函数 if(Qpoint.xpoint.y=0)/先判断该位置是否有棋子 if(player) printf(""); printf("a");/'a'表示蜂鸣声 Record(); else printf(""); printf("a"); Record(); goto_xy(point.x,point.y); void Play(char ch)/键盘的操作 移动光标 下棋和悔棋操作 if(ch=72)/的ASCLL码 光标上移 if(point.y<=3)point.y=20; elsepoint.y-; goto_xy(point.x,point.y); if(ch=75)/的ASCLL码 光标左移 if(point.x<=10)point.x=27; elsepoint.x-; goto_xy(point.x,point.y); if(ch=77)/的ASCLL码 光标右移 if(point.x>=27)point.x=10; elsepoint.x+; goto_xy(point.x,point.y); if(ch=80)/的ASCLL码 光标下移 if(point.y>=20)point.y=3; elsepoint.y+; goto_xy(point.x,point.y); if(ch=13)/回车键的ASCLL码下棋 PutDown(); if(button='b'|button='B') /悔棋的操作 ptr1=p; if(p!=head) p=p->fore; free(ptr1); point.x=p->coord.x; point.y=p->coord.y; go_back(point.x,point.y); int Judge() int count=0; int pp=player=0?2:1;/三目运算 for(int c=0; c<200; c+) for(int r=0; r<200; r+) if(Qrc!=pp) continue;/检查列 int rr=r; int cc=c; while(-cc>=3 &&Qrrcc=pp)count+; cc=c; while(+cc<23 &&Qrrcc=pp)count+; cc=c; if(count>=4) return pp;/检查行 count=0; while(-rr>=10 &&Qrrcc=pp)count+; rr=r; while(+rr<30 &&Qrrcc=pp)count+; rr=r; if(count>=4) return pp;/检查反斜边 count=0; cc-; rr-; while(cc>=3|rr>=10) &&Qrrcc=pp) count+; cc-; rr-; rr=r; cc=c; cc+; rr+; while(cc<23|rr<30) &&Qrrcc=pp) count+; cc+; rr+; rr=r; cc=c; if(count+1>=5) return pp;/检查正斜边 count=0; cc+; rr-; while(cc<23|rr>=10) &&Qrrcc=pp) count+; cc+; rr-; rr=r; cc=c; cc-; rr+; while(cc>=3|rr<30) &&Qrrcc=pp) count+; cc-; rr+; rr=r; cc=c; if(count+1>=5) return pp; count=0; return 0;int main(void)system("color 4E");printf("n 欢迎play五子棋!");goto_xy(15,3);printf("作者:小萝莉");goto_xy(17,5);printf(" HE·Honda");goto_xy(17,7);printf(" ZSSB");Sleep(3000);system("cls"); while(1) Init();/初始化 int winner=0; Welcome();/欢迎界面 while(1)/读取菜单选项 char choice=getch(); menu_choose(choice); if(startchoice!=0) break; if(startchoice=1)/选择人人对战 Draw(); goto_xy(17,22); printf("轮到甲方落子"); goto_xy(point.x,point.y); while(1) button=getch(); Play(button); /读取键盘的操作 移动光标 下棋和悔棋操作 if(button=27)/ESC的ASCLL码 if(MessageBox(NULL,TEXT("确定退出?"),TEXT(""),MB_ICONQUESTION|MB_OKCANCEL)=IDOK)system("cls");printf("n谢谢使用!n");return 0; if(button=13)/回车键ASCLL码按下后开始判断输赢 winner=Judge(); if(winner!=0) goto_xy(15,24); if(winner=2) printf("恭喜!甲方赢!n"); if(winner=1)