一、源码简介
这是一个可以进行五子棋游戏的小程序,采用C语言进行编写。
上下左右控制光标位置,空格下子,黑白棋交替进行。
编译环境:Dev-c++(采取纯C语言写法),没有调用第三方库,简单易学
二、运行截图
下棋时候界面,光标显示位置,空格下子
获胜后提示界面:
三、源码解析
先看主函数部分:
1 2 3 4 5 6 7 8 9 10 11 | int main() { system ( "title 五-子-棋" -- Dotcpp(dotcpp.com)); //设置文件标题 system ( "mode con cols=64 lines=32" ); //设置窗口大小 system ( "color E0" ); //设置背景颜色 while (1) { Gamerun(); //Gamerun的每一次循环是进行一整局游戏 } return 0; //实际用不上 } |
为了实现游戏的运行,我们需要完成Gamerun这个函数。下面是函数的框架,也是这个游戏运行的具体流程
初始化变量
循环对局:
{
打印棋盘;
以下循环{
接收键盘输入
输入为esc,退出游戏
输入为方向键,改变光标位置
输入为空格,走子并判定胜负,分出胜负则跳出循环
}
处理胜利事件
}
具体为代码就是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | void Gamerun() { int input; //定义键盘输入字符 Initialize(); //初始化变量 while (1) { Datatomap(); //将对局信息输入缓冲变量 Showcursor(); //将光标加入缓冲变量 Printmap(); //打印棋盘 input=getch(); //等待键盘输入指令 if (input==27) exit (0); //若输入是Esc,退出游戏 else if (input==32) //若输入空格,进行走子 { if (Put()) //走子成功 { if (Check()) break ; //检查是否分出胜负 else now=3-now; //切换走子方 } else ; //走子失败 } else if (input==0xE0) //如果按下的是方向键,会填充两次输入,第一次为0xE0表示按下的是控制键 { Movecursor(getch()); //利用第二次输入移动光标 } else ; } Showtest(); //将胜利文字输入缓冲区 Printmap(); //打印棋盘 getch(); //输入任意键开始下一局游戏 } |
getch()可以立即对输入的字符做出反应,而不像getchar()还要输入回车。
我们来详细看这几个步骤。
1. 初始化变量:随着项目的推进,我们自然会知道哪些数据需要初始化。先在这里放一个空函数。
1 2 3 | void Initialize() { } |
2. 打印棋盘:
我们可以通过打印制表符的方式来拼接整个棋盘。自然,我们需要一个缓冲变量来存储整个棋盘的信息,这个变量又是对局信息经过转换得到的。
int data[LENGTH][LENGTH];//储存对局信息,0空白,1黑子,2白子
char map[LENGTH*2+1][LENGTH*2+1][5];//需要打印的棋盘,包括边框,子的位置,以及文字
(建议缓冲变量不要像我这么写。这个三维字符数组的本意是想构建一个二维的字符串组,但在后续的操作中,就会发现这引起了可能的麻烦。有条件的同学可以试试,改变这个变量的形式,看看这个程序应该怎么写。)
设定将对局信息转化为缓冲变量的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | void Datatomap() //由对局信息生成棋盘信息 { int i,j; for (i=0;i<LENGTH*2+1;i++) { for (j=0;j<LENGTH*2+1;j++) { if (i==0&&j==0) strcpy (map[i][j], " ┏" ); else if (i==0&&j==LENGTH*2) strcpy (map[i][j], " ┓" ); else if (i==LENGTH*2&&j==0) strcpy (map[i][j], " ┗" ); else if (i==LENGTH*2&&j==LENGTH*2) strcpy (map[i][j], " ┛" ); else if (i==0&&j%2==0) strcpy (map[i][j], " ┯" ); else if (i==LENGTH*2&&j%2==0) strcpy (map[i][j], " ┷" ); else if ((i==0||i==LENGTH*2)&&j%2==1) strcpy (map[i][j], " ━" ); else if (j==0&&i%2==0) strcpy (map[i][j], " ┠ " ); else if (j==LENGTH*2&&i%2==0) strcpy (map[i][j], "┨" ); else if ((j==0||j==LENGTH*2)&&i%2==1) strcpy (map[i][j], " ┃" ); else if (i%2==0&&j%2==0&&data[i/2][j/2]==0) strcpy (map[i][j], "┼ " ); //注意棋盘变量的长度和棋局变量并不相同 else if (i%2==0&&j%2==0&&data[i/2][j/2]==1) strcpy (map[i][j], "●" ); else if (i%2==0&&j%2==0&&data[i/2][j/2]==2) strcpy (map[i][j], "○" ); else if (i%2==0&&j%2==1) strcpy (map[i][j], "—" ); else if (i%2==1&&j%2==0) strcpy (map[i][j], " │" ); else strcpy (map[i][j], " " ); } } } |
设定打印棋盘信息的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | void Printmap() //将棋盘信息打印至屏幕 { system ( "cls" ); //清理屏幕,准备写入 int i,j; char line[100]; //定义一个函数,存放当前输出行的数据 for (i=0;i<LENGTH*2+1;i++) //逐行输出棋盘数据 { strcpy (line, "" ); for (j=0;j<LENGTH*2+1;j++) { strcat (line,map[i][j]); //将输出拼接到该行的末尾 } printf ( "%s" ,line); //打印该行的数据 if (i<LENGTH*2) printf ( "\n" ); else ; } }这里设定每次打印一行的数据。如果利用双循环,调用 printf 次数过多,会变得很卡。 |
完成这一步之后,屏幕上应该已经能看到输出了。有可能字符的位置还是不大对,通过改变为map[i][j]赋值的函数内容,可以看出究竟是哪里出了问题,然后在适当的位置添加空格调整。
现在我们的成果是这样的。
之后每做一步,也可以看看显示界面的变化,这样方便及时修正程序当中的错误。
3. 接收键盘输入并处理命令:
如果输入为Esc,退出程序。
if(input==27)exit(0); //若输入是Esc,退出游戏
如果输入为方向键,切换光标指示的位置。
1 2 3 4 | else if (input==0xE0) //如果按下的是方向键,会填充两次输入,第一次为0xE0表示按下的是控制键 { Movecursor(getch()); //利用第二次输入移动光标 } |
这里需要定义一个函数,以显示光标位置。
1 2 3 4 5 6 7 | void Showcursor() //在棋盘信息中加入光标位置 { strcpy (map[x*2-1][y*2-1], " ┏" ); strcpy (map[x*2-1][y*2+1], " ┓" ); strcpy (map[x*2+1][y*2-1], " ┗" ); strcpy (map[x*2+1][y*2+1], " ┛" ); //在光标的周围加入制表符 } |
如果输入空格,执行走子程序。
1 2 3 4 5 6 7 8 9 10 | else if (input==32) //若输入空格,进行走子 { if (Put()) //走子成功 { if (Check()) break ; //检查是否分出胜负 else now=3-now; //切换走子方 } else ; //走子失败 } |
4. 走子并且检验胜负
走子后,修改棋局变量,打印棋盘。
1 2 3 4 5 6 7 8 9 10 11 12 | int Put() //走子 { if (data[x][y]==0) //若光标处无子 { data[x][y]=now; //改变棋局数据 Datatomap(); //由对局数据生成棋盘信息 Printmap; //打印棋盘 return 1; } else return 0; } |
定义检验是否分出胜负的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | int Check() //检查从落子点出发,八个方向该类型棋子的数目 { int a=0,b=0,c=0,d=0,i; //四个方向上连续子的数目 for (i=1;i<5;i++) { if (x+i>=0&&x+i<LENGTH&&data[x+i][y]==now)a++; else break ; } for (i=1;i<5;i++) { if (x-i>=0&&x-i<LENGTH&&data[x-i][y]==now)a++; else break ; } for (i=1;i<5;i++) { if (y+i>=0&&y+i<LENGTH&&data[x][y+i]==now)b++; else break ; } for (i=1;i<5;i++) { if (y-i>=0&&y-i<LENGTH&&data[x][y-i]==now)b++; else break ; } for (i=1;i<5;i++) { if (y+i<LENGTH&&x+i<LENGTH&&data[x+i][y+i]==now)c++; else break ; } for (i=1;i<5;i++) { if (y-i>=0&&x-i>=0&&data[x-i][y-i]==now)c++; else break ; } for (i=1;i<5;i++) { if (y-i>=0&&x+i<LENGTH&&data[x+i][y-i]==now)d++; else break ; } for (i=1;i<5;i++) { if (y+i<LENGTH&&x-i>=0&&data[x-i][y+i]==now)d++; else break ; } if (a==4||b==4||c==4||d==4) return 1; else return 0; } |
5. 处理胜利事件
在胜利时需要一个表达文本的功能。设定一个函数,通过改变缓冲变量间接打印文字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void Showtest() //胜利时加入文字 { if (now==1) { strcpy (map[15][14], "黑" ); strcpy (map[15][15], "胜" ); strcpy (map[15][16], "!" ); } else { strcpy (map[15][14], "白" ); strcpy (map[15][15], "胜" ); strcpy (map[15][16], "!" ); } } |
增加胜利文字,打印棋盘;输入任意键,重新开始游戏。
1 2 3 | Showtest(); //将胜利文字输入缓冲区 Printmap(); //打印棋盘 getch(); //输入任意键开始下一局游戏 |
四,完整代码下载
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程