资源描述
C语言课程设计报告
题目: 简单的c五子棋
设计者: 方阳
专业班级: 软件工程1303班
学号: 311309060324
指导老师: 刘洁
所属系部: 计算机学院
2014年6月25日
河南理工大学1、设计任务书
1.1、 题目与要求:
本人计划编写一个简单的c五子棋,应用C语言编写程序可以在计算机上实现二人对弈五子棋功能。二人对弈五子棋程序由图像生成、光标移动与落子、判断胜负和系统帮助等子程序构成;程序中应用了结构体、数组、全局变量、按键处理和图形编程等元素和语句。程序通过棋盘和棋子图像生成、二人移子与落子和判断胜负等功能的实现,在计算机上实现了二人五子棋对弈。
1.2、 本系统涉及的知识点:
结构、数组、循环、函数、分支、
2、功能设计
根据功能需求,将程序分为图形显示、玩家控制、胜负判断和玩家计分四个模块,以下分析各模块的需求。
图形显示模块:程序开始运行时,给出欢迎及帮助界面;游戏开始后要求生成19×19的棋盘图像,并在棋盘上方提示当前落子方棋子颜色,游戏进行过程中,要求实时显示棋盘上已落下的棋子;分出胜负后,要求给出游戏结束画面。
玩家控制模块:程序开始时,需玩家确定而后开始游戏;游戏过程中,两个玩家通过不同的按键移动光标,选择落子;游戏结束时,有玩家选择是否开始新棋局。
胜负判断模块:实时监测棋盘上棋子,一旦某一色棋子出现五子连线,终止游戏程序,并着色连成一线的五子,弹出该色玩家胜出界面。
玩家计分模块:一方玩家再胜利后通过对文件的操作进行计分,并输出于计分板上。
2.1、算法设计
根据程序需求分析结果,可以得出程序的总体结构图如图。
2.2部分模块流程图
程序流程图
3、程序代码设计
3.1 图形模块
①initgr() /* BGI初始化 */
{
int gd = DETECT, gm = 0; /* 和gd = VGA,gm = VGAHI是同样效果 */
registerbgidriver(EGAVGA_driver);/* 注册BGI驱动后可以不需要.BGI文件的支持运行 */
initgraph(&gd, &gm, "");
}
② welcome() /*欢迎界面*/
{ initgr() ; /*初始化*/
clearviewport(); /*清屏*/
setcolor(2); /*设置颜色*/
settextstyle(0,0,6); /*样式*/
outtextxy(180,180,"Welcome");/*位置以及内容*/
settextstyle(0,0,2); /*文本样式*/
outtextxy(240,420,"press any key to continue");/*位置以及内容*/
getch(); /*press any key to continue*/
}
③ help() /*帮助界面,基本操作同welcome();函数*/
{
initgr();
clearviewport();
setcolor(2);
settextstyle(0,0,2);
outtextxy(60,100,"P1 move; W S A D ");
outtextxy(60,120," set : space ");
outtextxy(60,180,"P2 move: up down left right ");
outtextxy(60,200," set: Enter");
outtextxy(60,240,"You can press Backspace to pull back!") ;
outtextxy(60,280,"When you want to quit the game,");
outtextxy(60,300,"press Esc.");
outtextxy(220,440,"press any key to continue");
getch();
}
④ bye() /*say bye~@!,实现功能同上*/
{
initgr();
clearviewport();
setcolor(4) ;
settextstyle(0,0,6);
outtextxy(130,180,"Thank you!");
settextstyle(0,0,2);
outtextxy(240,420,"press any key to quit");
getch();
}
⑤ board()/*画棋盘*/
{
setcolor(14); /*线色*/
setfillstyle(1,6); /*样式*/
bar(120,50,520,450); /*棋盘底*/
for(k=0;k<21 ;k++)
{
line(120+20*k,50,120+20*k,450) ; /*竖线*/
line(120,50+20*k,520,50+20*k) ; /*横线*/
}
setfillstyle(1,9) ;
bar(540,200,620,300); /*1p2p两边框*/
bar(20,200,100,300);
setcolor(2);
settextstyle(3,0,4);
outtextxy(50,210,"1p");
outtextxy(570,210,"2p");
delay(50000);
show(); /*显示动态变色效果的标题*/
moveto(320,250); /*初始化中心位置*/
border(); /*画定位框*/
p1turn(); /*p1开始落子,游戏开始*/
}
⑥white() /*画白棋*/
{
setcolor(15);
setfillstyle(1,15);
circle(getx(),gety(),9);
floodfill(getx(),gety(),15);
}
black() /*画黑棋*/
{
setcolor(0);
setfillstyle(1,0);
circle(getx(),gety(),9);
floodfill(getx(),gety(),0);
}
green() /*用粉色着色连珠时的棋子*/
{
setcolor(13);
setfillstyle(1,13);
circle(getx(),gety(),9);
floodfill(getx(),gety(),13);
}
⑦
p1turn()/*画左边边框内白棋子*/
{
setcolor(15);
setfillstyle(1,15);
circle(60,250,9);
floodfill(60,250,15);
settextstyle(3,0,4);
outtextxy(20,280,"white turn");
}
p2turn() /*右边边框内黑色棋子*/
{
setcolor(0);
setfillstyle(1,0);
circle(580,250,9);
floodfill(580,250,0);
settextstyle(3,0,4);
outtextxy(540,280,"black turn");
}
p1del()/*不是1turn时候抹去左边边框内棋子*/
{ setfillstyle(1,9) ;
floodfill(60,250,9);
setcolor(9);
settextstyle(3,0,4);
outtextxy(20,280,"white turn");
}
p2del()/*不是2turn时候抹去右边边框内棋子*/
{
setfillstyle(1,9);
floodfill(580,250,9) ;
setcolor(9);
settextstyle(3,0,4);
outtextxy(540,280,"black turn");
}
⑧
border() /*画定位框*/
{
setcolor(14);
line(getx()-8,gety()-9,getx()-3,gety()-9); /*通过画线函数画出定位框*/
line(getx()+8,gety()-9,getx()+3,gety()-9);
line(getx()-8,gety()-9,getx()-8,gety()-4);
line(getx()+8,gety()-9,getx()+8,gety()-4);
line(getx()-8,gety()+9,getx()-3,gety()+9);
line(getx()+8,gety()+9,getx()+3,gety()+9);
line(getx()-8,gety()+9,getx()-8,gety()+4);
line(getx()+8,gety()+9,getx()+8,gety()+4);
}
delborder() /*除去定位框*/
{
setcolor(6); /*本质在于颜色覆盖*/
line(getx()-8,gety()-9,getx()-3,gety()-9); line(getx()+8,gety()-9,getx()+3,gety()-9);
line(getx()-8,gety()-9,getx()-8,gety()-4);
line(getx()+8,gety()-9,getx()+8,gety()-4);
line(getx()-8,gety()+9,getx()-3,gety()+9);
line(getx()+8,gety()+9,getx()+3,gety()+9);
line(getx()-8,gety()+9,getx()-8,gety()+4);
line(getx()+8,gety()+9,getx()+8,gety()+4);
}
⑨ drawmat(char *mat,int matsize,int x,int y,int color)/*点阵图所用标准输出方式*/
{
int i,j,k,m;
m=(matsize-1)/8+1;
for(j=0;j<matsize;j++)
for(i=0;i<m;i++)
for(k=0;k<8;k++)
if(mat[j*m+i]&(0x80>>k))
putpixel(x+i*8+k,y+j,color);
}
show() /*动态点阵图中文闪烁效果*/
{
for(n=0;n<3;n++)
{ delay(10000);
drawmat(wu32K,32,240,0,3);/*输出点阵图的“五”字*/
delay(10000);
drawmat(zi32K,32,290,0,3);
delay(10000);
drawmat(qi32K,32,340,0,3);
delay(10000);
drawmat(wu32K,32,240,0,2);
delay(10000);
drawmat(zi32K,32,290,0,2);
delay(10000);
drawmat(qi32K,32,340,0,2);
}
3.2 玩家操作模块
①
p1move() /*玩家1操作*/
{
switch(bioskey(0)) /*bioskey函数对键盘操作*/
{
case W: /*上移*/
所有if语句作用在于如果落子即将溢出边框,让其改变位置到另一端,例如移动棋盘最上端,若继续向上移动,则移动至棋盘最下端对应位置
{if(gety()<90)
{delborder();moveto(getx(),430);}
delborder();moverel(0,-20);border();p1move();}
case A: /*左移*/
{ if(getx()<160)
{delborder();moveto(500,gety());}
delborder();moverel(-20,0);border();p1move();}
case S: /*下移*/
{if(gety()>410)
{delborder();moveto(getx(),70);}
delborder();moverel(0,20);border();p1move();}
case D: /*右移*/
{ if(getx()>480)
{delborder();moveto(140,gety());}
delborder();moverel(20,0);border();p1move();}
case SP: /*落子*/
{if(a[getx()][gety()]!=6)
/*判定如果棋盘此处无子才能下,否则重新调用p1move函数*/
{p1move();}
delborder(); /*去除定位框*/
white(); /*画棋子*/
a[getx()][gety()]=1;/*将所在位置对应的数组点赋值为1*/
win(); /*判断胜负*/
border(); /*在新的位置画出定位框*/
对左右两边提示框操作,提示轮到2p进行操作,并调用p2move()函数
p1del();
p2turn();
p2move();}
case ESC: / *结束游戏*/
{bye();closegraph(); exit(0);}
case BACK : /*悔棋操作*/
{back();p1del();p2turn();p2move();}
default: p1move();
}
}
②
p2move() /*玩家2操作*/
P2move(); 函数与p1move();函数基本作用相同
{
switch(bioskey(0))
{
case UP:
{if(gety()<90)
{delborder();moveto(getx(),430);}
delborder();moverel(0,-20);border();p2move();}
case LEFT:
{if(getx()<160)
{delborder();moveto(500,gety());}delborder();moverel(-20,0);border();p2move();}
case DOWN:
{if(gety()>410)
{delborder();moveto(getx(),70);}delborder();moverel(0,20);border();p2move();}
case RIGHT:
{ if(getx()>480)
{delborder();moveto(140,gety());}delborder();moverel(20,0);border();p2move();}
case ENTER:
{if(a[getx()][gety()]!=6)
{p2move();}delborder();black();a[getx()][gety()]=0;win();border();p2del();p1turn();p1move();}
default: p2move();
case ESC:
{bye();closegraph(); exit(0);}
case BACK :
{back();p2del();p1turn();p1move();}
}
}
③
back() /*悔棋功能*/
{
setcolor(6);
setfillstyle(1,6);
circle(getx(),gety(),9);
floodfill(getx(),gety(),6); /*通过将现有棋子覆盖与棋盘底色相同的颜色来实现*/
setcolor(14) ;
line(getx()-9,gety(),getx()+9,gety()) ;
因为覆盖棋盘颜色后对应的棋谱线条颜色被覆盖,所以通过画线函数补回被覆盖负分的线色
line(getx(),gety()-9,getx(),gety()+9) ;
}
3.3胜负判断模块
①
win() /*胜负判断主程序*/
{
for(i=120;i<540;i=i+20)
for(j=50;j<470;j=j+20)/*通过循环扫描整个棋盘判定胜负*/
{
if((a[i][j]+a[i+20][j]+a[i+40][j]+a[i+60][j]+a[i+80][j]==5)||(a[i][j]+a[i][j+20]+a[i][j+40]+a[i][j+60]+a[i][j+80]==5)|| (a[i][j]+a[i+20][j+20]+a[i+40][j+40]+a[i+60][j+60]+a[i+80][j+80]==5)||(a[i][j+80]+a[i+20][j+60]+a[i+40][j+40]+a[i+60][j+20]+a[i+80][j]==5))
/*如果棋盘上任何位置数组对应的值之和为5,则白方5子连珠*/
{ if(a[i][j]+a[i+20][j]+a[i+40][j]+a[i+60][j]+a[i+80][j]==5)
{ moveto(i,j); green();delay(10000);moveto(i+20,j);green(); delay(10000);moveto(i+40,j);green();delay(10000);moveto(i+60,j);green();delay(10000);moveto(i+80,j);green();delay(10000);
}
else if(a[i][j]+a[i][j+20]+a[i][j+40]+a[i][j+60]+a[i][j+80]==5)
{
moveto(i,j); green();delay(10000);moveto(i,j+20);green(); delay(10000);moveto(i,j+40);green();delay(10000);moveto(i,j+60);green();delay(10000);moveto(i,j+80);green();delay(10000);
}
else if(a[i][j]+a[i+20][j+20]+a[i+40][j+40]+a[i+60][j+60]+a[i+80][j+80]==5)
{moveto(i,j); green();delay(10000);moveto(i+20,j+20);green(); delay(10000);moveto(i+40,j+40);green();delay(10000);moveto(i+60,j+60);green();delay(10000);moveto(i+80,j+80);green();delay(10000);
}
else if(a[i][j+80]+a[i+20][j+60]+a[i+40][j+40]+a[i+60][j+20]+a[i+80][j]==5)
{moveto(i,j+80); green();delay(10000);moveto(i+20,j+60);green(); delay(10000);moveto(i+40,j+40);green();delay(10000);moveto(i+60,j+20);green();delay(10000);moveto(i+80,j);green();delay(10000);
}
delscore1();p1_point_input2();p1_point_output() ;
p1win(); }
if((a[i][j]+a[i+20][j]+a[i+40][j]+a[i+60][j]+a[i+80][j]==0)||(a[i][j]+a[i][j+20]+a[i][j+40]+a[i][j+60]+a[i][j+80]==0)|| (a[i][j]+a[i+20][j+20]+a[i+40][j+40]+a[i+60][j+60]+a[i+80][j+80]==0)||(a[i][j+80]+a[i+20][j+60]+a[i+40][j+40]+a[i+60][j+20]+a[i+80][j]==0))
{ if(a[i][j]+a[i+20][j]+a[i+40][j]+a[i+60][j]+a[i+80][j]==0)
{ moveto(i,j); green();delay(10000);moveto(i+20,j);green(); delay(10000);moveto(i+40,j);green();delay(10000);moveto(i+60,j);green();delay(10000);moveto(i+80,j);green();delay(10000);
}
else if(a[i][j]+a[i][j+20]+a[i][j+40]+a[i][j+60]+a[i][j+80]==0)
{
moveto(i,j); green();delay(10000);moveto(i,j+20);green(); delay(10000);moveto(i,j+40);green();delay(10000);moveto(i,j+60);green();delay(10000);moveto(i,j+80);green();delay(10000);
}
else if(a[i][j]+a[i+20][j+20]+a[i+40][j+40]+a[i+60][j+60]+a[i+80][j+80]==0)
{moveto(i,j); green();delay(10000);moveto(i+20,j+20);green(); delay(10000);moveto(i+40,j+40);green();delay(10000);moveto(i+60,j+60);green();delay(10000);moveto(i+80,j+80);green();delay(10000);
}
else if(a[i][j+80]+a[i+20][j+60]+a[i+40][j+40]+a[i+60][j+20]+a[i+80][j]==0)
{moveto(i,j+80); green();delay(10000);moveto(i+20,j+60);green(); delay(10000);moveto(i+40,j+40);green();delay(10000);moveto(i+60,j+20);green();delay(10000);moveto(i+80,j);green();delay(10000);
}
delscore2(); p2_point_input2();p2_point_output() ;p2win(); }
}
}
②
p1win()
{
setcolor(15) ;
settextstyle(0,0,2);
此处2个函数用于1p或2p获胜后的告知以及询问是否开始新的一局,调用restart();函数以继续或退出。
outtextxy(10,110,"1p win");
outtextxy(240,460,"Restart?(Y/N)");
restart();
}
p2win()
{
setcolor(15) ;
settextstyle(0,0,2);
outtextxy(535,110,"2p win");
outtextxy(240,460,"Restart?(Y/N)");
restart();
}
③
restart()
{
通过对键盘的操作,提取Y或N以确定是否开始新的一局。
switch(bioskey(0))
{case Y:
{main();}
case N:
{bye();closegraph(); exit(0);}
default:restart();
}
}
④
initialization()
{ for(i=120;i<540;i++)
for(j=50;j<470;j++) /*循环初始化棋盘对应的数组所在位置值为6*/
{
a[i][j]=6;
}
}
3.4 文件操作模块
point() /*用于整合所有计分的函数,方便操作*/
之所以做这么多函数来实现文件操作的计分,是因为我发现C语言中对文件操作似乎存在bug,如果放在一个函数中经常出现文件无法正常读取,无法正常写入等现象,即使拆分成如此多函数,也依旧偶尔会出现以上提到的一些小bug。
{
p1_point_input();
p1_point_output() ;
p2_point_input();
p2_point_output() ;
}
p1_point_input() /*1p计分文件读取*/
首先打开文件,然后取出里面的数字赋值于ch变量,再用另一个函数打开文件并对ch进行自增操作,最后将ch写入文件。
{
FILE *fp;
fp=fopen("score1.txt","r");
ch=getw(fp);
fclose(fp);
}
p1_point_input2() /*1p计分文件输入*/
{
FILE *fp;
fp=fopen("score1.txt","r+");
ch=ch+1;
putw(ch,fp);
fclose(fp);
}
p1_point_output() /*1p计分输出*/
{
int i=0; char sh,score[10];
FILE *fp;
通过再次打开文件,循环扫描,输出文件中的数值并设置其样式。
fp=fopen("score1.txt", "r");
for(; (sh=fgetc(fp))!=EOF; )
{
score[i++]=sh;
}
score[i]='\0';
setcolor(12);
settextstyle(3,0,4);
outtextxy(40,320,"score");
outtextxy(55,340,score);
fclose(fp);
}
p2_point_input()
玩家2的计分模块,和玩家1的相同,就不再重复介绍了。
{
FILE *fp;
fp=fopen("score2.txt","r");
dh=getw(fp);
fclose(fp);
}
p2_point_input2()
{
FILE *fp;
fp=fopen("score2.txt","r+");
dh=dh+1;
putw(dh,fp);
fclose(fp);
}
p2_point_output()
{
int i=0; char fh,score[10];
FILE *fp;
fp=fopen("score2.txt", "r");
for(; (fh=fgetc(fp))!=EOF; )
{
score[i++]=fh;
}
score[i]='\0';
setcolor(12);
settextstyle(3,0,4);
outtextxy(560,320,"score");
outtextxy(575,340,score);
fclose(fp);
依旧通过循环读取文件内比分,然后通过颜色覆盖从而达到分数改变功能,这么操作十分复杂,体现了C语言图形编程下的不完善性。
delscore1() /*删除当前1p分数*/
{
int i=0; char sh,score[10];
FILE *fp;
fp=fopen("score1.txt", "r");
for(; (sh=fgetc(fp))!=EOF; )
{
score[i++]=sh;
}
score[i]='\0';
setcolor(0);
settextstyle(3,0,4);
outtextxy(40,320,"score");
outtextxy(55,340,score);
fclose(fp);
}
delscore2() /*删除当前2p分数*/
{
int i=0; char fh,score[10];
FILE *fp;
fp=fopen("score2.txt", "r");
for(; (fh=fgetc(fp))!=EOF; )
{
score[i++]=fh;
}
score[i]='\0';
setcolor(0);
settextstyle(3,0,4);
outtextxy(560,320,"score");
outtextxy(575,340,score);
}
4、程序设计总结
经过这次课程设计,我发现在很多地方的知识都不足,很多函数和技巧都要从网络上或其他书籍上找,可能学计算机要看看很多很多书,尤其在大学,很多情况下都需要自学,我想在以后的学习和实践工作中,会多读多看多练的,努力让自己的实力得到提高,在这个小游戏中,不能调用鼠标,只能通过输入坐标点位置,这是一个很大的不足,以后学习了一定要修改的更好!
5、参考文献
[1] 湛为芳.C语言程序设计技术.清华大学出版社,2006
[2] 谭浩强.C程序设计(第三版).清华大学出版社,2005
[3] Kennth A.reek. C和指针 (徐波译)
[4] 另外还使用了百度中文搜索引擎
展开阅读全文