大家好,今天要讲的是三子棋,大家小时候应该都玩过吧,今天我来教大家如何用C语言来做游戏。
首先,要把代码进行模块化分组。
像这样:
为什么要这样做呢?
第一、有利于处理代码,让代码看起来不那么冗长复杂。
第二、模块化有利于让你更好的调试代码,发现代码的问题。
(悄悄告诉你:很多大厂程序员都在用哦!!!)
好,接下来,进入正题:
下棋首先要有什么?
OS:
要有棋子其实要先打印菜单了,然后在打印棋盘,确定棋子,判断输赢等等。
让我们一步一步来看看吧。
这是一个游戏小菜单,1是玩游戏,0是退出
void menu()
{
printf("*****************************************\n");
printf("*************** 1 . play ****************\n");
printf("*************** 0 . exit ****************\n");
printf("*****************************************\n");
}
好了接下来要把它放到主函数里面去调用它(test.c)
int main()
{
int input = 0;
do
{
menu();//这里是调用上面的menu函数
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();//这里是游戏的具体实现
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
讲解一下:看到这么多代码不要慌,这里都是一些很基础的东西,都是大家的老朋友了。
第一步:把主函数和return 0的框架写出来,如果不会写去baidu查一下Hello World,然后要定义并初始化一个input,接收输入的数字;
第二步:用do——while,并把刚刚写好的
(菜单)menu函数调用一下,加上一个printf函数,提醒我们选择0或1,并用scanf输入,input接收;第三步:在do——while里面我们要用switch语句,case 1就是输入1进行游戏,case 0就是退出游戏,default代表输入的数字不是0和1,所以我们要用printf提醒别人,让他重新输入。break是跳出当前循环。
第四步:如果玩完了还想再来一次怎么办?所以在do和while的括号里放入了input,这样的话,input的为真,就会一直循环,直到input为假。
tips:在C语言中0为假,非0为真。
菜单做好了,接下来我们继续
1.棋盘(test.c)
void game() { //创建棋盘 char board[ROW][COL] = { 0 }; //初始化棋盘 Initboard(board, ROW, COL); //打印棋盘 Displayboard(board, ROW, COL); }
game.h
#define ROW 3 #define COL 3 //初始化棋盘 void Initboard(char board[ROW][COL], int row, int col); //打印棋盘 void Displayboard(char board[ROW][COL], int row, int col);
game.c
void Initboard(char board[ROW][COL], int row, int col) { int m = 0; int n = 0; for (m = 0; m < row; m++) { for (n = 0; n < col; n++) { board[m][n] = ' '; } } } void Displayboard(char board[ROW][COL], int row, int col) { int m = 0; for (m = 0; m < row; m++) { int n = 0; for (n = 0; n < col; n++) { printf(" %c ", board[m][n]); if (n < col - 1) printf("|"); } printf("\n"); if (m < row - 1) { int n = 0; for (n = 0; n < row; n++) { printf("---"); if (n < col - 1) printf("|"); } printf("\n"); } }
这里一共三个步骤:
第一步:创建棋盘,定义一个二维数组board,然后我们要在game.h中定义二维数组的行和列,行为ROW,列为COL。
第二步:初始化棋盘,我们用Initboard进行初始化棋盘,返回类型为void,小写的row和col是Initboard函数的形参。在game.h中声明函数,我们需要把函数拿到game.c中实现。很简单,定义两个变量,两层for循环,让我们的的棋盘初始化为空格。第一层for循环,m初始化后,开始和我们的行比较,比较后使用,再加1循环。第二次循环一样,不过是列的循环。
第三步:打印棋盘,由于我们要看到棋盘,所以需要把棋盘打印出来,用返回值为void的Displayboard函数实现。同样把它放到game.h中声明一下,在game.c中具体实现。
具体细节:同样是用for循环,我们先看元素的打印,前面就两层for循环确定位置,打印的东西很重要哦,我用了空格+%c+空格的方式打印行上的元素,然后用|分隔开,但是这个|是有条件的,最后那一列不打印,最后再换个行,这样看起来就比较美观。然后是分隔符的打印,这个就比较简单了,一个for循环遍历行打印分隔符,再加上一个if函数判断一下,打印|保持一致,最后换个行,完美。
这里我们完成了棋盘,接下来我们要开始下棋了 :
和上面一样,我们要用函数的方式进行。
我们先想出函数的功能,再去实现函数(test.c)
void game() { char board[ROW][COL] = { 0 }; //初始化棋盘 Initboard(board, ROW, COL); //打印棋盘 Displayboard(board, ROW, COL); //下棋 while (1) { PlayerMove(board, ROW, COL);//玩家下棋 Displayboard(board, ROW, COL);//打印棋盘 ComputerMove(board, ROW, COL);//电脑下棋 Displayboard(board, ROW, COL);//打印棋盘 } int main() { srand((unsigned int)time(NULL));//随机数设置 int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择!\n"); break; } } while (input); return 0; }
函数声明(game.h)
void PlayerMove(char board[ROW][COL], int row, int col); void ComputerMove(char board[ROW][COL], int row, int col);
函数功能实现(game.c)
void PlayerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("玩家下棋:>\n"); while (1) { printf("请输入坐标:>"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '*'; break; } else { printf("坐标被占用,不能下棋\n"); } } else printf("坐标非法,请重新输入!\n"); } } void ComputerMove(char board[ROW][COL], int row, int col) { printf("电脑下棋\n"); int x = 0; int y = 0; while (1) { x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } }
我这里主要写了玩家和电脑对弈的代码,所以比较简单,但是有不少细节,我来讲解一下:
这里也是分为三个步骤:
第一步:玩家下棋:我们定义一个PlayerMove来实现,把棋盘,行和列的信息传给它并声明一下。由于数组的下标是从0到1的,我们新定义一个X和一个Y放到while(1)循环,然后我们用if判断限制输入,重点来了,因为玩家不一定知道数组的知识,所以我在这里把x和y从1开始,然后再来一个if把x-1和y-1放入判断是否为空格,是空格就可以下,下完就break,不是空格就提示,然后重新循环,如果输入坐标不在第一个if判断的范围,提示坐标非法,重新循环。
第二步:电脑下棋:我们定义一个ComputerMove来实现,把棋盘,行和列的信息传给它并声明一下。电脑下棋很简单,直接一个if判断x和y放进去判断不是空格就行。细心的小伙伴发现,这里有个rand是什么意思啊?大家注意,这个rand是随机数的意思,它的范围是0到rand_max(32767)。然后有个设置它的东西叫srand,你们可以看到我在主函数中写出来了,那这个time又是什么意思呢?time是时间戳,我们让它返回一个空指针,然后再强制类型转换成(无符号整型)。rand % row和rand % col是让电脑取随机再0-2之间,避免数组越界。
第三步:测试!测试!测试!玩家下棋用*表示,电脑下棋用#表示,我们第三步要测试一下,我们能不能正常下棋,如果不能正常下棋,要确定是不是if条件出问题了或者是数组越界了。这是最重要的一步,任何人都会写出bug,不用慌张,要仔细去检查,然后在总结,下次就不会再次出现bug了,这样不断反复,慢慢自己就会变强。
好了,到这里,我们基本完成了80%以上,但是还有一个步骤,那就是判断输赢的过程:
这里我规定了一下: 玩家赢————‘*‘ 电脑赢————‘#’ 平局————’Q‘ 游戏继续————‘C’
上面的值大家都可以修改,只要你愿意设置什么样的就设置什么样的,这只是我这个代码的思路。
同样我用了函数来帮助我实现(test.c)
void game() { char win = 0; char board[ROW][COL] = { 0 }; //初始化棋盘 Initboard(board, ROW, COL); //打印棋盘 Displayboard(board, ROW, COL); //下棋 while (1) { PlayerMove(board, ROW, COL); win = Iswin(board, ROW, COL); //判断输赢 if (win != 'C') { break; } Displayboard(board, ROW, COL); ComputerMove(board, ROW, COL); win = Iswin(board, ROW, COL); //判断输赢 if (win != 'C') { break; } Displayboard(board, ROW, COL); } if (win == '*') { printf("玩家赢\n"); Displayboard(board, ROW, COL); } else if (win == '#') { printf("电脑赢\n"); Displayboard(board, ROW, COL); } else { printf("平局\n"); Displayboard(board, ROW, COL); } }
函数声明(game.h)
int IsFull(char board[ROW][COL], int row, int col); char Iswin(char board[ROW][COL], int row, int col);
函数实现(game.c)
int IsFull(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { return 0; } } } return 1; } char Iswin(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ') { return board[i][1]; } } for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ') { return board[1][j]; } } if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ') { return board[1][1]; } if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') { return board[1][1]; } if (IsFull(board, row, col) == 1) { return 'Q'; } return 'C'; }
这里同样我分三个步骤讲解:
第一步:我们用正常逻辑思考,玩家下完棋要不要判断输赢,电脑下完棋要不要判断输赢,要是吧。所以我先用if判断整个棋盘能不能继续下棋,如果不能我们跳出while(1),来到我们下面的第二个if,如果是*就算玩家赢了,是#就算电脑赢了,如果都不是,那我们刚刚判断过了游戏不能继续,所以只能是平局。好了,这是第一步。
第二步:我在test.c里有两个函数没有实现,一个是IsWin和IsFull,好我们在game.c中实现一下。
首先IsFull,两层for循环遍历元素,if判断是否有空格,有空格棋盘没满返回0,没有空格就是棋盘满了返回1,返回类型int。其次是IsWin,三子棋赢的规则就是三个棋子连在一起就赢了,所以我们分为行,列,对角线三种情况,首先分析行,一个for循环表示行号,然后加上if判断,这判断条件很有意思,首先是第i行的三个元素两两相等,啪,有的同学写到这里就完了,注意啊,这里要加上不能等于空格!!! 不然永远都是你赢!!!返回值当然是第i行上的任意一个元素了。
第三步:好了,行写完,列的也差不多,for循环表示列号,加上if判断,第i列的三个元素两两相等,然后一样不等于空格。接下来是对角线,对角线有两条,一样是 对角线上的元素两两相等,然后不等于空格。最后,如果都判断完了 ,三种情况都没人赢,那再判断一下IsFull是否等于1,如果等于,那就是平局返回Q,如果不是就返回C 。
注意:细心的小伙伴肯定发现,我这里行和列,对角线的判断输赢写成了固定的3行3列了,如果想写成N字棋的话,把这块改一下,我相信这对于评论区的前排来说还是很简单的。
好了,到这里,我们全部的代码就大功告成了,其实编程也不是很难,对不对?
接下来把代码整合一下:
(test.c)
#include "game.h" void menu() { printf("*****************************************\n"); printf("*************** 1 . play ****************\n"); printf("*************** 0 . exit ****************\n"); printf("*****************************************\n"); } void game() { char win = 0; char board[ROW][COL] = { 0 }; //初始化棋盘 Initboard(board, ROW, COL); //打印棋盘 Displayboard(board, ROW, COL); //下棋 while (1) { PlayerMove(board, ROW, COL); win = Iswin(board, ROW, COL);//判断输赢 if (win != 'C') { break; } Displayboard(board, ROW, COL); ComputerMove(board, ROW, COL); win = Iswin(board, ROW, COL);//判断输赢 if (win != 'C') { break; } Displayboard(board, ROW, COL); } if (win == '*') { printf("玩家赢\n"); Displayboard(board, ROW, COL); } else if (win == '#') { printf("电脑赢\n"); Displayboard(board, ROW, COL); } else { printf("平局\n"); Displayboard(board, ROW, COL); } } int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择!\n"); break; } } while (input); return 0; }
函数声明(game.h)
#include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 3 #define COL 3 //初始化棋盘 void Initboard(char board[ROW][COL], int row, int col); //打印棋盘 void Displayboard(char board[ROW][COL], int row, int col); void PlayerMove(char board[ROW][COL], int row, int col); void ComputerMove(char board[ROW][COL], int row, int col); int IsFull(char board[ROW][COL], int row, int col); char Iswin(char board[ROW][COL], int row, int col);
game.c
#include "game.h" void Initboard(char board[ROW][COL], int row, int col) { int m = 0; int n = 0; for (m = 0; m < row; m++) { for (n = 0; n < col; n++) { board[m][n] = ' '; } } } void Displayboard(char board[ROW][COL], int row, int col) { int m = 0; for (m = 0; m < row; m++) { int n = 0; for (n = 0; n < col; n++) { printf(" %c ", board[m][n]); if (n < col - 1) printf("|"); } printf("\n"); if (m < row - 1) { int n = 0; for (n = 0; n < row; n++) { printf("---"); if (n < col - 1) printf("|"); } printf("\n"); } } } void PlayerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("玩家下棋:>\n"); while (1) { printf("请输入坐标:>"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '*'; break; } else { printf("坐标被占用,不能下棋\n"); } } else printf("坐标非法,请重新输入!\n"); } } void ComputerMove(char board[ROW][COL], int row, int col) { printf("电脑下棋\n"); int x = 0; int y = 0; while (1) { x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } int IsFull(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { return 0; } } } return 1; } char Iswin(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ') { return board[i][1]; } } for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ') { return board[1][j]; } } if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ') { return board[1][1]; } if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') { return board[1][1]; } if (IsFull(board, row, col) == 1) { return 'Q'; } return 'C'; }
最后说明一下啊,我把所有的头文件放在了game.h里面,这样只要引头文件game.h就行,time函数要<time.h>,printf函数尧<stdio.h>,rand函数要<stdlib.h>。
感谢大家的观看,后期我会分析一些基础C语言的干货知识(太忙了,555)。
如果大家喜欢,点上关注不迷路。有错误一定要告诉我,我会很开心的,谢谢大家。