(我们这里以五子棋为例子,具体的值可以自行修改哦)
1.要做一个下棋的小游戏,首先先做一个简陋的游戏菜单界面哈哈=)
void menu()//游戏菜单界面
{
printf("****************************************************\n");
printf("*********输入1开始游戏 输入0退出游戏************\n");
printf("****************************************************\n");
}
2.既然模拟下棋的过程,就要有先后手的区别,因为我们是高贵的真人玩家(bushi),所以由我们决定先后手
printf("请选择先手还是后手:(1为先手,2为后手)\n");
scanf("%d", &n);
system("cls"); //清屏操作 为了界面更好看=)
if (n == 1) {
while (1) { //用while(1)的原因后面会提到
;//过程
}
}
if (n == 2) {
while (1) {
;//过程
}
}
3.在游戏开始之前,我们还得初始化一下棋盘上的棋子,方便后续的操作
void init(int m, int n) //初始化为空格
{
int i = 0, j = 0;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
a[i][j] = ' ';
}
}
}
原因在后面会说到(说是后面,其实就在下一点)
4.. 之后就要生成我们的棋盘啦
(我做了两个版本的棋盘) 虽然好像没什么意义,但是好看捏=)
//井字形棋盘
void display( int m, int n)
{
int i = 0,j=0;
for (i = 0; i < m; i++)//以给定的行数 重复打印
{
//一行的内容
for (j = 0; j < n; j++)
{
printf(" %c ", a[i][j] );
if (j < n - 1)
printf("|");
}//打印出有数据的一行
printf("\n");
if (i < h - 1)
{
for (j = 0; j < n; j++)
{
printf("---");
if (j < n - 1)
printf("|");
}//打印出分割行
}
printf("\n");
}
}
//封闭型棋盘
void display(int m, int n)
{
int i = 0, j = 0;
for (i = 0; i < m + 1; i++)//以给定的行数 重复打印
{
for (j = 0; j < n + 1; j++)
{
printf("|");
if (j < n)
{
printf("---");
}
}//打印出分割行
printf("\n");
if (i < n)
{//一行的内容
for (j = 0; j < n + 1; j++)
{
printf("|");
if (j < n)
printf(" %c ", a[i][j]);
}//打印出有数据的一行
}
printf("\n");
}
}
- 这些线线的打印就看你的需求了,使用for循环实现
- 这里就用到了我们前一步的初始化捏,不要想当然的觉得直接打印空格就好了哦,因为我们落子的过程就是给a数组赋值
5.我们先模拟玩家先手的情况
(图为玩家已经下了第一步)
*是玩家棋子,-是电脑棋子
void playermove(int m, int n) {
printf("玩家下棋\n");
printf("请输入你要下棋的位置:\n");
display(h, l);
int q = 0, w = 0;
while (1) {
scanf("%d%d", &q, &w);
if ((q >= 1 && q <= m) || (w >= 1 && w <= n)) { //对于玩家来说
if (a[q - 1][w - 1] == ' ') { //对于电脑,坐标应-1
a[q - 1][w - 1] = '*';
system("cls");
break;
}
else {
printf("该位置被占用,请重新输入\n");
}
}
else {
printf("该位置不存在,请重新输入\n");
}
}
}
这是玩家进行下棋的代码
(需要进行对输入坐标的判断)
- 输入正确,进行落子,并退出该while循环
- 输入的坐标已经被占用或超出范围,进行提示,并提醒重新输入(通过while(1)实现重新输入的操作)
6.玩家落子后,该电脑进行(但电脑不能无端落子,需要生成坐标)
为了保证游戏的公平性,使用随机数生成
//随机在空处下
void computermove(int m, int n) {
system("cls");
printf("电脑下棋\n");
int q = 0, w = 0;
while (1) {
q = rand() % m; //用随机数生成坐标
w = rand() % n;
if (a[q - 1][w - 1] == ' ') { //下标要-1哦
a[q - 1][w - 1] = '-';
break;
}
}
}
- 用rand()函数生成随机数,其头文件为<stdlib.h>
(这个绿色真好看捏) - 用rand()生成随机数时,需要和srand()函数配套使用
- srand()是一个随机数生成器(即设置一个随机起点),其头文件为<stdlib.h>,其括号内为unsigned整型,所以需要强制类型转换(我们得到的是整型)
- 其实我们将它称为随机数是不够准确的,其实是伪随机数,是一串已经生成好的数字序列
- 当你不设置随机数生成器时,得到的数字序列将会相同,因为系统会自动给你配置一个srand(1)
- 若为srand(1),则会固定出现某个不变的数字,因为其应该出现变量的位置上是1,一个固定的数字决定了这串数字有固定的起点,那么你得到的数字序列为同一串,即每次打印出来的结果都相同
- (就我的理解来说,应该是srand()从无限个门里选取某一个门作为起点,每个门里都有一串没有规律的数字序列,因此若是1,即你一直选取的都是序号为1的门,所以你得到的数字相同)(哈哈,个人理解,轻喷,本人只是一个可怜弱小的大一学生)
- 所以我们应该自己设置一个一直会变化的变量,使用时间是最稳妥和最常用的
- 我们一般能获取到的就是系统时间,使用time()函数来获得,其头文件为<time.h>,其括号内需要一个指针,我们一般使用NULL(因为我们不需要这个指针出现什么效果)
- time()得到的值被称为时间戳,是以秒为单位,用1970年1月1日0点减去现在的时间,这样我们可以保证我们每次得到的值都是不同的,即起点不同
- 我去查了查,为啥非得是1970年1月1日0点
因为用32位来表示时间的最大间隔是68年,而最早出现的UNIX操作系统考虑到计算机产生的年代和应用的时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始时间)
了解就好,如果有人问你,就可以浅浅装个b啦=)帮别人答疑解惑啦=)
可恶,扯远了,看我怎么圆回来
- 前面不是在聊随机数嘛,随机数也是有范围的,rand()的范围为0-x7fff(32767)
- 如果我们要生成给定范围的随机数,假设你要生成[n,m]的随机数,可以用rand() % (m-n+1)+n
7.然后就是最最最最最.......(省略10086个最)重要的了!!!判断胜负!
下棋嘛,也就那么几种获胜形式,要么一横排,要么一竖列,要么对角线(当然憋忘了对角线有两种)
废话不多说,直接看代码叭=)
int win(int m, int n) {
//玩家赢,*
//电脑赢,-
//平局,p
int i = 0, j = 0;
int count = 0;
for (i = 0; i < m; i++) {
for (j = 0; j < n - 1; j++) {
if (a[i][j] == a[i][j + 1] && a[i][j] != ' ' ) { //两个为一组比较//横排相等
count++;
}
}
if (count == m-1) { //因为两个一组,所以-1
return a[i][j];
}
count = 0;
}
for (j = 0; j < n; j++) {
for (i = 0; i < m - 1; i++) {
if (a[i][j] == a[i + 1][j] && a[i][j] != ' ') { //竖列相等
count++;
}
}
if (count == n-1 ) {
return a[i][j];
}
count = 0;
}
j = 0;
for (i = 0; i < m-1; i++) {
if (a[i][j] == a[i + 1][j + 1] && a[i][j] != ' ') { //"\"对角线
count++;
}
j++;
}
if (count == m-1) {
return a[i][j];
}
count = 0;
j = j - 1;
for (i = 0; i < m-1; i++) {
if (a[i][j] == a[i + 1][j + 1] && a[i][j] != ' ' ) { //"/"对角线
count++;
}
j--;
}
if (count == m-1 ) {
return a[i][j+1];
}
if (full(h, l) == 1) { //判断是否格子被占满,若未出胜负前格子已满,即为平局
return 'p';
}
return 'g'; //都不满足就继续,"go"
}
- 就按照胜利的条件,分为三大块的判断,第三块的又分为俩小块
- 我使用的是:如果两个格子内的字符如果相等就+1,然后如果等于棋盘的行数-1(两个一组哦!所以-1)就是胜利(别忘了排除两个字符都是空格的情况)
- 最后,还得判断棋盘上是否被占满了,原因注释里有哦
int full(int m, int n) { //判断是否满
int i = 0, j = 0;
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++) {
if (a[i][j] == ' ')
return 0;
}
}
return 1;
}
- 如果没满就返回0,满了就返回0,并且win()返回'p'(注意哦,咱们win()不是有返回值吗,在外层函数里就需要用这个返回值来分成不同的情况
- 其中不是有return a[i][j] 吗,这里就是比较巧妙的一点了,我们可以直接返回满足占满一行或一列或对角线的该字符,用if来分支时,就可以用if(接收返回值的变量=='*'),这样就是玩家赢,因为玩家使用的是*棋子;电脑赢就是if(接收返回值的变量=='-'),我们设置的电脑棋子就是-哦 =)
- (当然这个代码电脑赢很难,因为对面没有思考,只是将按生成的随机数落子)
8.游戏的主体内容实现就已经完成了,之后就是将返回的字符进行分支处理
我直接按代码执行的顺序贴代码叭
int main()//大框架
{
test();
return 0;
}
void test()//游戏流程
{
int a;
menu();
do
{
scanf("%d", &a);
switch (a)
{
case 1:
printf("开始我们的游戏吧!\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新输入\n");
}
} while (a);
}
void game()//游戏实现
{
srand((unsigned int)time(NULL));
init(h, l);//初始化棋盘(放入空格)
int n = 0;
char e = 0;
//游戏过程
printf("请选择先手还是后手:(1为先手,2为后手)\n");
scanf("%d", &n);
system("cls");
if (n == 1) {
while (1) {
playermove(h, l);
e = win(h, l);
if (e != 'g') {
break;
}
computermove(h, l);
display(h, l);
e = win(h, l);
if (e != 'g') {
break;
}
}
}
if (n == 2) {
while (1) {
computermove(h, l);
display(h, l);
e = win(h, l);
if (e != 'g') {
break;
}
playermove(h, l);
display(h, l);//打印棋盘
e = win(h, l);
if (e != 'g') {
break;
}
}
}
if (e == '*') {
display(h, l);
printf("恭喜你赢得胜利\n");
printf("是否要重新开始:");
}
if (e == '-') {
display(h, l);
printf("很遗憾你输了\n");
printf("是否要重新开始:");
}
if (e == 'p') {
display(h, l);
printf("本局为平局\n");
printf("是否要重新开始:");
}
}
- 用do while循环可以通过你玩完一局后输入的数字进行判断,如果是1就再来一局,0就退出游戏
- 在适当的地方使用清屏,可以让执行过程更加美观(但我调整不好,所以就只保留了一个,呜呜)
system("cls")可以实现清屏,其头文件是<stdlib.h>
9.其他的
我写的多文件形式,记得要用" "引用自定义的头文件哦
#define h 5
#define l 5
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
char a[h][l];//建立棋盘信息
void init(int m, int n);
void display(int m, int n);
void playermove(int m, int n);
void computermove(int m, int n);
int win(int m, int n);
int full(int m, int n);
- 这是我设置的头文件,标题说的可拓展就是用宏定义实现的,你可以随意改h和l的值哦=),改成几就是几子棋
- 我test.c中放的就是8里的代码,是游戏的整体框架,
虽然套了好几层 - game.c顾名思义,就是实现游戏内容的文件
完美落幕啦~~~撒花~~~
感谢您的观看! =)
这是孩子第一篇博客,有不妥之处可以直接告诉我哦,我会很感谢您的建议(鞠躬)