猴子也会懂的C语言五子棋的实现方法详解
2017-08-02 22:06
316 查看
正在学习C语言的博主自编了一个五子棋程序,下边将自己在coding中遇到的一些问题与解决办法发出来,以求帮助和博主一样迷茫的C语言入门者
代码如下(有些凌乱,重要部分在下边都有解说哦:-))
这里主要总结一下在程序设计中遇到的两个难点:
1.光标控制与方向键的识别
2.胜负判断
解决办法
光标控制使用了头文件window.h自带的函数(由于所学甚浅,这部分主要靠百度,不过百度上的方法还是多少有点杂乱,没有解释,我尽量用简单的语言概括一下.
用到的相关windows.h知识有:
函数:
GetStdHandle(STD_OUTPUT_HANDLE)用在main函数中,作用是从窗口中读取标准输入的句柄(句柄概念本人不太了解,这里说的是本人的理解).
GetConsoleScreenBufferInfo(hout,&csbi)使用设置光标坐标函数的前提.
SetConsoleCursorPosition(hout,coord)设置光标坐标
类:
句柄:如HANDLE hout :调用windows中关于
COORD:内含两个整形变量,分别为xxx.X和xxx.Y(xxx是类的名称)
概括来讲只要包含上述函数后即可通过SetConsoleCursorPosition(hout,coord)随意改变光标位置;
方向键的识别
原理:方向键的键码:上-3272 左:-3275 下:-3277 右:-3280(十进制)
声明两个char变量ch1与ch2(由于char的最大值为一个char不能完全接受方向键的键值所以需要两个才能完全接收一个方向键并进行判定),接收方向键的ASCII码后进行两次判断,判定玩家按了哪个方向键,并对坐标进行改变,在后一步中通过SetConsoleCursorPosition将光标位置改变.当然还需要判断光标是否超出棋盘的范围. :-)
胜负判断
对于五子棋这样一个入门性的程序,网络中常见的胜负判断方式有两种,一种是通过直接扫描一遍棋盘判定有没有连着五个的棋子,一种是直接扫描上一个落下的棋子周围的棋子有无连成串的棋子.本程序采用了第一种判断方式,相比第二种确实在计算上略显复杂,但好在相比第二种方法更适合像博主这样的小白实现.关于第二种方法可以参考百度,上边有很详细的解释.下边关于第一种办法进行详解.
验证横排的办法非常简单,这里声明一个变量token进行标记,每次用开始的棋子与后边四个棋子分别比较,若五个棋子相等则token将等于4,此时改变游戏循环的条件使游戏循环终止.用i表示排,o表示行即可遍历所有的横排连着五个棋子的组合.验证竖排时比葫芦画瓢即可.
当然要注意开始的棋子不能等于’+’!
验证斜排的办法要稍微麻烦,下面是检验/方向的代码.
不短,对吧,因为对于一个15x15的正方形点阵来说,它一共有15+15-1个斜排!,好在因为有8个斜排因为容纳棋子数不超过4个可以不予考虑;那也还有21个! 所以我们将这些斜排通过对角线分成两个部分,通过两个循环解决问题!看着也不麻烦嘛!胜负判断到此结束!
好了,接下来要做的就只有调整程序的结构,让你的新游戏显得更聪明,更有交互性,比如给它加上一个启动画面,加上几个小彩蛋了!大工告成!
欢迎在评论里与博主进行讨论,以后博主还会写更多更深入的代码与大家交流,愿与大家在学习编程的道路上共同进步! :-0 BYE~
PS:这个程序有一个小小的有趣BUG,你能发现吗?
代码如下(有些凌乱,重要部分在下边都有解说哦:-))
#include<stdio.h> //定义:奇数回合为米方回合,偶数回合为圈方回合// #include<windows.h> HANDLE hout; COORD coord; int test,test1; char QP[15][15],stop,stop2; int i,o,j; int a,b; int turn; int move_(); int main() { int token; turn=1; coord.X=0; coord.Y=0; hout=GetStdHandle(STD_OUTPUT_HANDLE); test=1; test1=1; printf("你正在玩一个叫做五子棋的古老游戏\n"); printf("按回车进行游戏"); stop=getch(); system("cls"); if(stop!=13) { printf("爱玩不玩!"); test=0; printf("\n你可以按任意键滚"); stop=getch(); } system("cls"); for(i=0;i<15;i++) { for(o=0;o<15;o++) { QP[i][o]='+'; } } while(test==1) //游戏循环// { for(i=0;i<15;i++) //通过双重循环打印棋盘// { for(o=0;o<15;o++) { printf("%c ",QP[i][o]); } printf("\n"); } printf("\n先走的是\"*\",你叫星星\n后走的是\"o\",你叫圈圈"); a=move_(); //打印完毕 下边进行胜负判断,原理大概是通过依次判断相邻五个位置的值是否相等// for(i=0;i<15;i++)//验证横排// { for(o=0;o<9;o++) { token=0; for(j=1;j<=4;j++) { if(QP[i][o]==QP[i][o+j]&&QP[i][o]!='+') token++; if(token==4) test=0; } } } for(o=0;o<15;o++)//验证竖排// { for(i=0;i<9;i++) { token=0; for(j=1;j<=4;j++) { if(QP[i][o]==QP[i+j][o]&&QP[i][o]!='+') token++; if(token==4) test=0; } } } for(i=4;i<=14;i++)//验证'/'对角线// { test1=i-3; //对于每个斜行需要验证的次数// for(o=0;o<test1;o++) { token=0; for(j=1;j<=4;j++) { if(QP[0+o][i-o]==QP[0+o+j][i-j-o]&&QP[0+o][i-o]!='+') token++; if(token==4) test=0; } } } for(i=1;i<=10;i++) { test1=10-i; //对于每个斜行需要验证的次数// for(o=0;o<test1;o++) { token=0; for(j=1;j<=4;j++) { if(QP[14-o][i+o]==QP[14-o-j][i+j+o]&&QP[14-o][i+o]!='+') token++; if(token==4) test=0; } } } for(i=0;i<=10;i++)//验证'\'对角线// { test1=11-i; //对于每个斜行需要验证的次数// for(o=0;o<test1;o++) { token=0; for(j=1;j<=4;j++) { if(QP[o][i+o]==QP[o+j][i+j+o]&&QP[o][i+o]!='+') token++; if(token==4) test=0; } } } for(i=4;i<=13;i++) { test1=i-3; //对于每个斜行需要验证的次数// for(o=0;o<test1;o++) { token=0; for(j=1;j<=4;j++) { if(QP[14-o][i-o]==QP[14-o-j][i-o-j]&&QP[14-o][i-o]!='+') token++; if(token==4) test=0; } } } if(test==0) { system("cls"); for(i=0;i<15;i++) //通过双重循环打印棋盘// { for(o=0;o<15;o++) { printf("%c ",QP[i][o]); } printf("\n"); } turn--; coord.X=0; coord.Y=15; SetConsoleCursorPosition(hout,coord); if(turn%2==0) printf("圈圈赢了!"); else printf("星星赢了!"); printf("\n按键继续"); stop=getch(); } system("cls"); } } int move_() { b=1; while(b==1) { SetConsoleCursorPosition(hout,coord); char ch1,ch2; CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hout,&csbi); ch1=getch(); if(ch1==-32) { ch2=getch(); switch(ch2) { case 72: if(coord.Y!=0) coord.Y=coord.Y-1; else coord.Y=14; break; case 75: if(coord.X!=0) coord.X=coord.X-2; else coord.X=28; break; case 77: if(coord.X!=28) coord.X=2+coord.X; else coord.X=0; break; case 80: if(coord.Y!=14) coord.Y=1+coord.Y; else coord.Y=0; break; } SetConsoleCursorPosition(hout,coord); } if(ch1==13) { int X=0; X=coord.X/2; if(turn%2==0) QP[coord.Y][X]='O'; else QP[coord.Y][X]='*'; b=0; turn++; } } return 0; }
这里主要总结一下在程序设计中遇到的两个难点:
1.光标控制与方向键的识别
2.胜负判断
解决办法
光标控制使用了头文件window.h自带的函数(由于所学甚浅,这部分主要靠百度,不过百度上的方法还是多少有点杂乱,没有解释,我尽量用简单的语言概括一下.
用到的相关windows.h知识有:
函数:
GetStdHandle(STD_OUTPUT_HANDLE)用在main函数中,作用是从窗口中读取标准输入的句柄(句柄概念本人不太了解,这里说的是本人的理解).
GetConsoleScreenBufferInfo(hout,&csbi)使用设置光标坐标函数的前提.
SetConsoleCursorPosition(hout,coord)设置光标坐标
类:
句柄:如HANDLE hout :调用windows中关于
COORD:内含两个整形变量,分别为xxx.X和xxx.Y(xxx是类的名称)
概括来讲只要包含上述函数后即可通过SetConsoleCursorPosition(hout,coord)随意改变光标位置;
方向键的识别
原理:方向键的键码:上-3272 左:-3275 下:-3277 右:-3280(十进制)
char ch1,ch2; ch1=getch(); if(ch1==-32) { ch2=getch(); switch(ch2) { case 72: if(coord.Y!=0) coord.Y=coord.Y-1; else coord.Y=14; break; case 75: if(coord.X!=0) coord.X=coord.X-2; else coord.X=28; break; case 77: if(coord.X!=28) coord.X=2+coord.X; else coord.X=0; break; case 80: if(coord.Y!=14) coord.Y=1+coord.Y; else coord.Y=0; break; }
声明两个char变量ch1与ch2(由于char的最大值为一个char不能完全接受方向键的键值所以需要两个才能完全接收一个方向键并进行判定),接收方向键的ASCII码后进行两次判断,判定玩家按了哪个方向键,并对坐标进行改变,在后一步中通过SetConsoleCursorPosition将光标位置改变.当然还需要判断光标是否超出棋盘的范围. :-)
胜负判断
对于五子棋这样一个入门性的程序,网络中常见的胜负判断方式有两种,一种是通过直接扫描一遍棋盘判定有没有连着五个的棋子,一种是直接扫描上一个落下的棋子周围的棋子有无连成串的棋子.本程序采用了第一种判断方式,相比第二种确实在计算上略显复杂,但好在相比第二种方法更适合像博主这样的小白实现.关于第二种方法可以参考百度,上边有很详细的解释.下边关于第一种办法进行详解.
for(i=4;i<15;i++)//验证横排// { for(o=0;o<9;o++) { token=0; for(j=1;j<=4;j++) { if(QP[i][o]==QP[i][o+j]&&QP[i][o]!='+') token++; if(token==4) test=0; } } }
验证横排的办法非常简单,这里声明一个变量token进行标记,每次用开始的棋子与后边四个棋子分别比较,若五个棋子相等则token将等于4,此时改变游戏循环的条件使游戏循环终止.用i表示排,o表示行即可遍历所有的横排连着五个棋子的组合.验证竖排时比葫芦画瓢即可.
当然要注意开始的棋子不能等于’+’!
验证斜排的办法要稍微麻烦,下面是检验/方向的代码.
for(i=4;i<=14;i++)//验证'/'对角线// { test1=i-3; //对于每个斜行需要验证的次数// for(o=0;o<test1;o++) { token=0; for(j=1;j<=4;j++) { if(QP[0+o][i-o]==QP[0+o+j][i-j-o]&&QP[0+o][i-o]!='+') token++; if(token==4) test=0; } } } for(i=1;i<=10;i++) { test1=10-i; //对于每个斜行需要验证的次数// for(o=0;o<test1;o++) { token=0; for(j=1;j<=4;j++) { if(QP[14-o][i+o]==QP[14-o-j][i+j+o]&&QP[14-o][i+o]!='+') token++; if(token==4) test=0; } } }
不短,对吧,因为对于一个15x15的正方形点阵来说,它一共有15+15-1个斜排!,好在因为有8个斜排因为容纳棋子数不超过4个可以不予考虑;那也还有21个! 所以我们将这些斜排通过对角线分成两个部分,通过两个循环解决问题!看着也不麻烦嘛!胜负判断到此结束!
好了,接下来要做的就只有调整程序的结构,让你的新游戏显得更聪明,更有交互性,比如给它加上一个启动画面,加上几个小彩蛋了!大工告成!
欢迎在评论里与博主进行讨论,以后博主还会写更多更深入的代码与大家交流,愿与大家在学习编程的道路上共同进步! :-0 BYE~
PS:这个程序有一个小小的有趣BUG,你能发现吗?
相关文章推荐
- C语言单链表实现方法详解
- C语言接口与实现方法实例详解
- 工业PID控制方法的C语言实现详解
- 数值计算方法与算法:C语言实现利用Gauss消元法解方程组
- DPMI在C语言中的实现方法浅析
- 类五子棋的小游戏(c语言实现)
- 猴子吃桃问题(C语言实现)
- C语言实现MATLAB 6.5中M文件的方法
- C语言常用函数的底层实现方法
- NAT详解及CISCO路由器上的实现方法
- C语言实现多态的一种方法
- C语言使用openSSL库DES模块实现加密功能详解
- 详解Oracle用户解锁命令的两则实现方法
- 简单代码实现LCD菜单(C语言的实现方法---加强版 有视频教程)
- NP901: NAT详解及CISCO路由器上的实现方法
- Linux下获得系统时间的C语言的实现方法
- Linux下获得系统时间的C语言的实现方法
- Linux下获得系统时间的C语言的实现方法
- CRC算法原理及C语言实现(介绍了3种方法)
- 几种排序方法的实现 (C语言)